1 /* 2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #ifndef lint 15 static char id[] = "@(#)$Id: readcf.c,v 8.382.4.43 2001/08/14 23:08:13 ca Exp $"; 16 #endif /* ! lint */ 17 18 #include <sendmail.h> 19 20 21 #if NETINET || NETINET6 22 # include <arpa/inet.h> 23 #endif /* NETINET || NETINET6 */ 24 25 #define SECONDS 26 #define MINUTES * 60 27 #define HOUR * 3600 28 #define HOURS HOUR 29 30 static void fileclass __P((int, char *, char *, bool, bool)); 31 static char **makeargv __P((char *)); 32 static void settimeout __P((char *, char *, bool)); 33 static void toomany __P((int, int)); 34 35 /* 36 ** READCF -- read configuration file. 37 ** 38 ** This routine reads the configuration file and builds the internal 39 ** form. 40 ** 41 ** The file is formatted as a sequence of lines, each taken 42 ** atomically. The first character of each line describes how 43 ** the line is to be interpreted. The lines are: 44 ** Dxval Define macro x to have value val. 45 ** Cxword Put word into class x. 46 ** Fxfile [fmt] Read file for lines to put into 47 ** class x. Use scanf string 'fmt' 48 ** or "%s" if not present. Fmt should 49 ** only produce one string-valued result. 50 ** Hname: value Define header with field-name 'name' 51 ** and value as specified; this will be 52 ** macro expanded immediately before 53 ** use. 54 ** Sn Use rewriting set n. 55 ** Rlhs rhs Rewrite addresses that match lhs to 56 ** be rhs. 57 ** Mn arg=val... Define mailer. n is the internal name. 58 ** Args specify mailer parameters. 59 ** Oxvalue Set option x to value. 60 ** Pname=value Set precedence name to value. 61 ** Vversioncode[/vendorcode] 62 ** Version level/vendor name of 63 ** configuration syntax. 64 ** Kmapname mapclass arguments.... 65 ** Define keyed lookup of a given class. 66 ** Arguments are class dependent. 67 ** Eenvar=value Set the environment value to the given value. 68 ** 69 ** Parameters: 70 ** cfname -- configuration file name. 71 ** safe -- TRUE if this is the system config file; 72 ** FALSE otherwise. 73 ** e -- the main envelope. 74 ** 75 ** Returns: 76 ** none. 77 ** 78 ** Side Effects: 79 ** Builds several internal tables. 80 */ 81 82 void 83 readcf(cfname, safe, e) 84 char *cfname; 85 bool safe; 86 register ENVELOPE *e; 87 { 88 FILE *cf; 89 int ruleset = -1; 90 char *q; 91 struct rewrite *rwp = NULL; 92 char *bp; 93 auto char *ep; 94 int nfuzzy; 95 char *file; 96 bool optional; 97 int mid; 98 register char *p; 99 long sff = SFF_OPENASROOT; 100 struct stat statb; 101 char buf[MAXLINE]; 102 char exbuf[MAXLINE]; 103 char pvpbuf[MAXLINE + MAXATOM]; 104 static char *null_list[1] = { NULL }; 105 extern u_char TokTypeNoC[]; 106 107 FileName = cfname; 108 LineNumber = 0; 109 110 if (DontLockReadFiles) 111 sff |= SFF_NOLOCK; 112 cf = safefopen(cfname, O_RDONLY, 0444, sff); 113 if (cf == NULL) 114 { 115 syserr("cannot open"); 116 finis(FALSE, EX_OSFILE); 117 } 118 119 if (fstat(fileno(cf), &statb) < 0) 120 { 121 syserr("cannot fstat"); 122 finis(FALSE, EX_OSFILE); 123 } 124 125 if (!S_ISREG(statb.st_mode)) 126 { 127 syserr("not a plain file"); 128 finis(FALSE, EX_OSFILE); 129 } 130 131 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 132 { 133 if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) 134 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 135 FileName); 136 if (LogLevel > 0) 137 sm_syslog(LOG_CRIT, NOQID, 138 "%s: WARNING: dangerous write permissions", 139 FileName); 140 } 141 142 #ifdef XLA 143 xla_zero(); 144 #endif /* XLA */ 145 146 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 147 { 148 if (bp[0] == '#') 149 { 150 if (bp != buf) 151 sm_free(bp); 152 continue; 153 } 154 155 /* do macro expansion mappings */ 156 translate_dollars(bp); 157 158 /* interpret this line */ 159 errno = 0; 160 switch (bp[0]) 161 { 162 case '\0': 163 case '#': /* comment */ 164 break; 165 166 case 'R': /* rewriting rule */ 167 if (ruleset < 0) 168 { 169 syserr("missing valid ruleset for \"%s\"", bp); 170 break; 171 } 172 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 173 continue; 174 175 if (*p == '\0') 176 { 177 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 178 break; 179 } 180 181 /* allocate space for the rule header */ 182 if (rwp == NULL) 183 { 184 RewriteRules[ruleset] = rwp = 185 (struct rewrite *) xalloc(sizeof *rwp); 186 } 187 else 188 { 189 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 190 rwp = rwp->r_next; 191 } 192 rwp->r_next = NULL; 193 194 /* expand and save the LHS */ 195 *p = '\0'; 196 expand(&bp[1], exbuf, sizeof exbuf, e); 197 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 198 sizeof pvpbuf, NULL, 199 ConfigLevel >= 9 ? TokTypeNoC : NULL); 200 nfuzzy = 0; 201 if (rwp->r_lhs != NULL) 202 { 203 register char **ap; 204 205 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 206 207 /* count the number of fuzzy matches in LHS */ 208 for (ap = rwp->r_lhs; *ap != NULL; ap++) 209 { 210 char *botch; 211 212 botch = NULL; 213 switch (**ap & 0377) 214 { 215 case MATCHZANY: 216 case MATCHANY: 217 case MATCHONE: 218 case MATCHCLASS: 219 case MATCHNCLASS: 220 nfuzzy++; 221 break; 222 223 case MATCHREPL: 224 botch = "$0-$9"; 225 break; 226 227 case CANONUSER: 228 botch = "$:"; 229 break; 230 231 case CALLSUBR: 232 botch = "$>"; 233 break; 234 235 case CONDIF: 236 botch = "$?"; 237 break; 238 239 case CONDFI: 240 botch = "$."; 241 break; 242 243 case HOSTBEGIN: 244 botch = "$["; 245 break; 246 247 case HOSTEND: 248 botch = "$]"; 249 break; 250 251 case LOOKUPBEGIN: 252 botch = "$("; 253 break; 254 255 case LOOKUPEND: 256 botch = "$)"; 257 break; 258 } 259 if (botch != NULL) 260 syserr("Inappropriate use of %s on LHS", 261 botch); 262 } 263 rwp->r_line = LineNumber; 264 } 265 else 266 { 267 syserr("R line: null LHS"); 268 rwp->r_lhs = null_list; 269 } 270 if (nfuzzy > MAXMATCH) 271 { 272 syserr("R line: too many wildcards"); 273 rwp->r_lhs = null_list; 274 } 275 276 /* expand and save the RHS */ 277 while (*++p == '\t') 278 continue; 279 q = p; 280 while (*p != '\0' && *p != '\t') 281 p++; 282 *p = '\0'; 283 expand(q, exbuf, sizeof exbuf, e); 284 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 285 sizeof pvpbuf, NULL, 286 ConfigLevel >= 9 ? TokTypeNoC : NULL); 287 if (rwp->r_rhs != NULL) 288 { 289 register char **ap; 290 291 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 292 293 /* check no out-of-bounds replacements */ 294 nfuzzy += '0'; 295 for (ap = rwp->r_rhs; *ap != NULL; ap++) 296 { 297 char *botch; 298 299 botch = NULL; 300 switch (**ap & 0377) 301 { 302 case MATCHREPL: 303 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 304 { 305 syserr("replacement $%c out of bounds", 306 (*ap)[1]); 307 } 308 break; 309 310 case MATCHZANY: 311 botch = "$*"; 312 break; 313 314 case MATCHANY: 315 botch = "$+"; 316 break; 317 318 case MATCHONE: 319 botch = "$-"; 320 break; 321 322 case MATCHCLASS: 323 botch = "$="; 324 break; 325 326 case MATCHNCLASS: 327 botch = "$~"; 328 break; 329 } 330 if (botch != NULL) 331 syserr("Inappropriate use of %s on RHS", 332 botch); 333 } 334 } 335 else 336 { 337 syserr("R line: null RHS"); 338 rwp->r_rhs = null_list; 339 } 340 break; 341 342 case 'S': /* select rewriting set */ 343 expand(&bp[1], exbuf, sizeof exbuf, e); 344 ruleset = strtorwset(exbuf, NULL, ST_ENTER); 345 if (ruleset < 0) 346 break; 347 348 rwp = RewriteRules[ruleset]; 349 if (rwp != NULL) 350 { 351 if (OpMode == MD_TEST) 352 printf("WARNING: Ruleset %s has multiple definitions\n", 353 &bp[1]); 354 if (tTd(37, 1)) 355 dprintf("WARNING: Ruleset %s has multiple definitions\n", 356 &bp[1]); 357 while (rwp->r_next != NULL) 358 rwp = rwp->r_next; 359 } 360 break; 361 362 case 'D': /* macro definition */ 363 mid = macid(&bp[1], &ep); 364 if (mid == 0) 365 break; 366 p = munchstring(ep, NULL, '\0'); 367 define(mid, newstr(p), e); 368 break; 369 370 case 'H': /* required header line */ 371 (void) chompheader(&bp[1], CHHDR_DEF, NULL, e); 372 break; 373 374 case 'C': /* word class */ 375 case 'T': /* trusted user (set class `t') */ 376 if (bp[0] == 'C') 377 { 378 mid = macid(&bp[1], &ep); 379 if (mid == 0) 380 break; 381 expand(ep, exbuf, sizeof exbuf, e); 382 p = exbuf; 383 } 384 else 385 { 386 mid = 't'; 387 p = &bp[1]; 388 } 389 while (*p != '\0') 390 { 391 register char *wd; 392 char delim; 393 394 while (*p != '\0' && isascii(*p) && isspace(*p)) 395 p++; 396 wd = p; 397 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 398 p++; 399 delim = *p; 400 *p = '\0'; 401 if (wd[0] != '\0') 402 setclass(mid, wd); 403 *p = delim; 404 } 405 break; 406 407 case 'F': /* word class from file */ 408 mid = macid(&bp[1], &ep); 409 if (mid == 0) 410 break; 411 for (p = ep; isascii(*p) && isspace(*p); ) 412 p++; 413 if (p[0] == '-' && p[1] == 'o') 414 { 415 optional = TRUE; 416 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 417 p++; 418 while (isascii(*p) && isspace(*p)) 419 p++; 420 } 421 else 422 optional = FALSE; 423 424 file = p; 425 q = p; 426 while (*q != '\0' && !(isascii(*q) && isspace(*q))) 427 q++; 428 if (*file == '|') 429 p = "%s"; 430 else 431 { 432 p = q; 433 if (*p == '\0') 434 p = "%s"; 435 else 436 { 437 *p = '\0'; 438 while (isascii(*++p) && isspace(*p)) 439 continue; 440 } 441 } 442 fileclass(mid, file, p, safe, optional); 443 break; 444 445 #ifdef XLA 446 case 'L': /* extended load average description */ 447 xla_init(&bp[1]); 448 break; 449 #endif /* XLA */ 450 451 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) 452 case 'L': /* lookup macro */ 453 case 'G': /* lookup class */ 454 /* reserved for Sun -- NIS+ database lookup */ 455 if (VendorCode != VENDOR_SUN) 456 goto badline; 457 sun_lg_config_line(bp, e); 458 break; 459 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */ 460 461 case 'M': /* define mailer */ 462 makemailer(&bp[1]); 463 break; 464 465 case 'O': /* set option */ 466 setoption(bp[1], &bp[2], safe, FALSE, e); 467 break; 468 469 case 'P': /* set precedence */ 470 if (NumPriorities >= MAXPRIORITIES) 471 { 472 toomany('P', MAXPRIORITIES); 473 break; 474 } 475 for (p = &bp[1]; *p != '\0' && *p != '='; p++) 476 continue; 477 if (*p == '\0') 478 goto badline; 479 *p = '\0'; 480 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 481 Priorities[NumPriorities].pri_val = atoi(++p); 482 NumPriorities++; 483 break; 484 485 case 'V': /* configuration syntax version */ 486 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 487 continue; 488 if (!isascii(*p) || !isdigit(*p)) 489 { 490 syserr("invalid argument to V line: \"%.20s\"", 491 &bp[1]); 492 break; 493 } 494 ConfigLevel = strtol(p, &ep, 10); 495 496 /* 497 ** Do heuristic tweaking for back compatibility. 498 */ 499 500 if (ConfigLevel >= 5) 501 { 502 /* level 5 configs have short name in $w */ 503 p = macvalue('w', e); 504 if (p != NULL && (p = strchr(p, '.')) != NULL) 505 *p = '\0'; 506 define('w', macvalue('w', e), e); 507 } 508 if (ConfigLevel >= 6) 509 { 510 ColonOkInAddr = FALSE; 511 } 512 513 /* 514 ** Look for vendor code. 515 */ 516 517 if (*ep++ == '/') 518 { 519 /* extract vendor code */ 520 for (p = ep; isascii(*p) && isalpha(*p); ) 521 p++; 522 *p = '\0'; 523 524 if (!setvendor(ep)) 525 syserr("invalid V line vendor code: \"%s\"", 526 ep); 527 } 528 break; 529 530 case 'K': 531 expand(&bp[1], exbuf, sizeof exbuf, e); 532 (void) makemapentry(exbuf); 533 break; 534 535 case 'E': 536 p = strchr(bp, '='); 537 if (p != NULL) 538 *p++ = '\0'; 539 setuserenv(&bp[1], p); 540 break; 541 542 #if _FFR_MILTER 543 case 'X': /* mail filter */ 544 milter_setup(&bp[1]); 545 break; 546 #endif /* _FFR_MILTER */ 547 548 default: 549 badline: 550 syserr("unknown configuration line \"%s\"", bp); 551 } 552 if (bp != buf) 553 sm_free(bp); 554 } 555 if (ferror(cf)) 556 { 557 syserr("I/O read error"); 558 finis(FALSE, EX_OSFILE); 559 } 560 (void) fclose(cf); 561 FileName = NULL; 562 563 /* initialize host maps from local service tables */ 564 inithostmaps(); 565 566 /* initialize daemon (if not defined yet) */ 567 initdaemon(); 568 569 /* determine if we need to do special name-server frotz */ 570 { 571 int nmaps; 572 char *maptype[MAXMAPSTACK]; 573 short mapreturn[MAXMAPACTIONS]; 574 575 nmaps = switch_map_find("hosts", maptype, mapreturn); 576 UseNameServer = FALSE; 577 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 578 { 579 register int mapno; 580 581 for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 582 { 583 if (strcmp(maptype[mapno], "dns") == 0) 584 UseNameServer = TRUE; 585 } 586 } 587 588 #ifdef HESIOD 589 nmaps = switch_map_find("passwd", maptype, mapreturn); 590 UseHesiod = FALSE; 591 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 592 { 593 register int mapno; 594 595 for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) 596 { 597 if (strcmp(maptype[mapno], "hesiod") == 0) 598 UseHesiod = TRUE; 599 } 600 } 601 #endif /* HESIOD */ 602 } 603 } 604 /* 605 ** TRANSLATE_DOLLARS -- convert $x into internal form 606 ** 607 ** Actually does all appropriate pre-processing of a config line 608 ** to turn it into internal form. 609 ** 610 ** Parameters: 611 ** bp -- the buffer to translate. 612 ** 613 ** Returns: 614 ** None. The buffer is translated in place. Since the 615 ** translations always make the buffer shorter, this is 616 ** safe without a size parameter. 617 */ 618 619 void 620 translate_dollars(bp) 621 char *bp; 622 { 623 register char *p; 624 auto char *ep; 625 626 for (p = bp; *p != '\0'; p++) 627 { 628 if (*p == '#' && p > bp && ConfigLevel >= 3) 629 { 630 /* this is an on-line comment */ 631 register char *e; 632 633 switch (*--p & 0377) 634 { 635 case MACROEXPAND: 636 /* it's from $# -- let it go through */ 637 p++; 638 break; 639 640 case '\\': 641 /* it's backslash escaped */ 642 (void) strlcpy(p, p + 1, strlen(p)); 643 break; 644 645 default: 646 /* delete leading white space */ 647 while (isascii(*p) && isspace(*p) && 648 *p != '\n' && p > bp) 649 p--; 650 if ((e = strchr(++p, '\n')) != NULL) 651 (void) strlcpy(p, e, strlen(p)); 652 else 653 *p-- = '\0'; 654 break; 655 } 656 continue; 657 } 658 659 if (*p != '$' || p[1] == '\0') 660 continue; 661 662 if (p[1] == '$') 663 { 664 /* actual dollar sign.... */ 665 (void) strlcpy(p, p + 1, strlen(p)); 666 continue; 667 } 668 669 /* convert to macro expansion character */ 670 *p++ = MACROEXPAND; 671 672 /* special handling for $=, $~, $&, and $? */ 673 if (*p == '=' || *p == '~' || *p == '&' || *p == '?') 674 p++; 675 676 /* convert macro name to code */ 677 *p = macid(p, &ep); 678 if (ep != p + 1) 679 (void) strlcpy(p + 1, ep, strlen(p + 1)); 680 } 681 682 /* strip trailing white space from the line */ 683 while (--p > bp && isascii(*p) && isspace(*p)) 684 *p = '\0'; 685 } 686 /* 687 ** TOOMANY -- signal too many of some option 688 ** 689 ** Parameters: 690 ** id -- the id of the error line 691 ** maxcnt -- the maximum possible values 692 ** 693 ** Returns: 694 ** none. 695 ** 696 ** Side Effects: 697 ** gives a syserr. 698 */ 699 700 static void 701 toomany(id, maxcnt) 702 int id; 703 int maxcnt; 704 { 705 syserr("too many %c lines, %d max", id, maxcnt); 706 } 707 /* 708 ** FILECLASS -- read members of a class from a file 709 ** 710 ** Parameters: 711 ** class -- class to define. 712 ** filename -- name of file to read. 713 ** fmt -- scanf string to use for match. 714 ** safe -- if set, this is a safe read. 715 ** optional -- if set, it is not an error for the file to 716 ** not exist. 717 ** 718 ** Returns: 719 ** none 720 ** 721 ** Side Effects: 722 ** 723 ** puts all lines in filename that match a scanf into 724 ** the named class. 725 */ 726 727 static void 728 fileclass(class, filename, fmt, safe, optional) 729 int class; 730 char *filename; 731 char *fmt; 732 bool safe; 733 bool optional; 734 { 735 FILE *f; 736 long sff; 737 pid_t pid; 738 register char *p; 739 char buf[MAXLINE]; 740 741 if (tTd(37, 2)) 742 dprintf("fileclass(%s, fmt=%s)\n", filename, fmt); 743 744 if (filename[0] == '|') 745 { 746 auto int fd; 747 int i; 748 char *argv[MAXPV + 1]; 749 750 i = 0; 751 for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) 752 { 753 if (i >= MAXPV) 754 break; 755 argv[i++] = p; 756 } 757 argv[i] = NULL; 758 pid = prog_open(argv, &fd, CurEnv); 759 if (pid < 0) 760 f = NULL; 761 else 762 f = fdopen(fd, "r"); 763 } 764 else 765 { 766 pid = -1; 767 sff = SFF_REGONLY; 768 if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail)) 769 sff |= SFF_SAFEDIRPATH; 770 if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR, 771 DontBlameSendmail)) 772 sff |= SFF_NOWLINK; 773 if (safe) 774 sff |= SFF_OPENASROOT; 775 if (DontLockReadFiles) 776 sff |= SFF_NOLOCK; 777 f = safefopen(filename, O_RDONLY, 0, sff); 778 } 779 if (f == NULL) 780 { 781 if (!optional) 782 syserr("fileclass: cannot open '%s'", filename); 783 return; 784 } 785 786 while (fgets(buf, sizeof buf, f) != NULL) 787 { 788 #if SCANF 789 char wordbuf[MAXLINE + 1]; 790 #endif /* SCANF */ 791 792 if (buf[0] == '#') 793 continue; 794 #if SCANF 795 if (sscanf(buf, fmt, wordbuf) != 1) 796 continue; 797 p = wordbuf; 798 #else /* SCANF */ 799 p = buf; 800 #endif /* SCANF */ 801 802 /* 803 ** Break up the match into words. 804 */ 805 806 while (*p != '\0') 807 { 808 register char *q; 809 810 /* strip leading spaces */ 811 while (isascii(*p) && isspace(*p)) 812 p++; 813 if (*p == '\0') 814 break; 815 816 /* find the end of the word */ 817 q = p; 818 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 819 p++; 820 if (*p != '\0') 821 *p++ = '\0'; 822 823 /* enter the word in the symbol table */ 824 setclass(class, q); 825 } 826 } 827 828 (void) fclose(f); 829 if (pid > 0) 830 (void) waitfor(pid); 831 } 832 /* 833 ** MAKEMAILER -- define a new mailer. 834 ** 835 ** Parameters: 836 ** line -- description of mailer. This is in labeled 837 ** fields. The fields are: 838 ** A -- the argv for this mailer 839 ** C -- the character set for MIME conversions 840 ** D -- the directory to run in 841 ** E -- the eol string 842 ** F -- the flags associated with the mailer 843 ** L -- the maximum line length 844 ** M -- the maximum message size 845 ** N -- the niceness at which to run 846 ** P -- the path to the mailer 847 ** R -- the recipient rewriting set 848 ** S -- the sender rewriting set 849 ** T -- the mailer type (for DSNs) 850 ** U -- the uid to run as 851 ** W -- the time to wait at the end 852 ** m -- maximum messages per connection 853 ** / -- new root directory 854 ** The first word is the canonical name of the mailer. 855 ** 856 ** Returns: 857 ** none. 858 ** 859 ** Side Effects: 860 ** enters the mailer into the mailer table. 861 */ 862 863 void 864 makemailer(line) 865 char *line; 866 { 867 register char *p; 868 register struct mailer *m; 869 register STAB *s; 870 int i; 871 char fcode; 872 auto char *endp; 873 extern int NextMailer; 874 875 /* allocate a mailer and set up defaults */ 876 m = (struct mailer *) xalloc(sizeof *m); 877 memset((char *) m, '\0', sizeof *m); 878 879 /* collect the mailer name */ 880 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 881 continue; 882 if (*p != '\0') 883 *p++ = '\0'; 884 if (line[0] == '\0') 885 { 886 syserr("name required for mailer"); 887 return; 888 } 889 m->m_name = newstr(line); 890 891 /* now scan through and assign info from the fields */ 892 while (*p != '\0') 893 { 894 auto char *delimptr; 895 896 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 897 p++; 898 899 /* p now points to field code */ 900 fcode = *p; 901 while (*p != '\0' && *p != '=' && *p != ',') 902 p++; 903 if (*p++ != '=') 904 { 905 syserr("mailer %s: `=' expected", m->m_name); 906 return; 907 } 908 while (isascii(*p) && isspace(*p)) 909 p++; 910 911 /* p now points to the field body */ 912 p = munchstring(p, &delimptr, ','); 913 914 /* install the field into the mailer struct */ 915 switch (fcode) 916 { 917 case 'P': /* pathname */ 918 if (*p == '\0') 919 syserr("mailer %s: empty path name", m->m_name); 920 else 921 m->m_mailer = newstr(p); 922 break; 923 924 case 'F': /* flags */ 925 for (; *p != '\0'; p++) 926 if (!(isascii(*p) && isspace(*p))) 927 setbitn(bitidx(*p), m->m_flags); 928 break; 929 930 case 'S': /* sender rewriting ruleset */ 931 case 'R': /* recipient rewriting ruleset */ 932 i = strtorwset(p, &endp, ST_ENTER); 933 if (i < 0) 934 return; 935 if (fcode == 'S') 936 m->m_sh_rwset = m->m_se_rwset = i; 937 else 938 m->m_rh_rwset = m->m_re_rwset = i; 939 940 p = endp; 941 if (*p++ == '/') 942 { 943 i = strtorwset(p, NULL, ST_ENTER); 944 if (i < 0) 945 return; 946 if (fcode == 'S') 947 m->m_sh_rwset = i; 948 else 949 m->m_rh_rwset = i; 950 } 951 break; 952 953 case 'E': /* end of line string */ 954 if (*p == '\0') 955 syserr("mailer %s: null end-of-line string", 956 m->m_name); 957 else 958 m->m_eol = newstr(p); 959 break; 960 961 case 'A': /* argument vector */ 962 if (*p == '\0') 963 syserr("mailer %s: null argument vector", 964 m->m_name); 965 else 966 m->m_argv = makeargv(p); 967 break; 968 969 case 'M': /* maximum message size */ 970 m->m_maxsize = atol(p); 971 break; 972 973 case 'm': /* maximum messages per connection */ 974 m->m_maxdeliveries = atoi(p); 975 break; 976 977 #if _FFR_DYNAMIC_TOBUF 978 case 'r': /* max recipient per envelope */ 979 m->m_maxrcpt = atoi(p); 980 break; 981 #endif /* _FFR_DYNAMIC_TOBUF */ 982 983 case 'L': /* maximum line length */ 984 m->m_linelimit = atoi(p); 985 if (m->m_linelimit < 0) 986 m->m_linelimit = 0; 987 break; 988 989 case 'N': /* run niceness */ 990 m->m_nice = atoi(p); 991 break; 992 993 case 'D': /* working directory */ 994 if (*p == '\0') 995 syserr("mailer %s: null working directory", 996 m->m_name); 997 else 998 m->m_execdir = newstr(p); 999 break; 1000 1001 case 'C': /* default charset */ 1002 if (*p == '\0') 1003 syserr("mailer %s: null charset", m->m_name); 1004 else 1005 m->m_defcharset = newstr(p); 1006 break; 1007 1008 case 'T': /* MTA-Name/Address/Diagnostic types */ 1009 /* extract MTA name type; default to "dns" */ 1010 m->m_mtatype = newstr(p); 1011 p = strchr(m->m_mtatype, '/'); 1012 if (p != NULL) 1013 { 1014 *p++ = '\0'; 1015 if (*p == '\0') 1016 p = NULL; 1017 } 1018 if (*m->m_mtatype == '\0') 1019 m->m_mtatype = "dns"; 1020 1021 /* extract address type; default to "rfc822" */ 1022 m->m_addrtype = p; 1023 if (p != NULL) 1024 p = strchr(p, '/'); 1025 if (p != NULL) 1026 { 1027 *p++ = '\0'; 1028 if (*p == '\0') 1029 p = NULL; 1030 } 1031 if (m->m_addrtype == NULL || *m->m_addrtype == '\0') 1032 m->m_addrtype = "rfc822"; 1033 1034 /* extract diagnostic type; default to "smtp" */ 1035 m->m_diagtype = p; 1036 if (m->m_diagtype == NULL || *m->m_diagtype == '\0') 1037 m->m_diagtype = "smtp"; 1038 break; 1039 1040 case 'U': /* user id */ 1041 if (isascii(*p) && !isdigit(*p)) 1042 { 1043 char *q = p; 1044 struct passwd *pw; 1045 1046 while (*p != '\0' && isascii(*p) && 1047 (isalnum(*p) || strchr("-_", *p) != NULL)) 1048 p++; 1049 while (isascii(*p) && isspace(*p)) 1050 *p++ = '\0'; 1051 if (*p != '\0') 1052 *p++ = '\0'; 1053 if (*q == '\0') 1054 { 1055 syserr("mailer %s: null user name", 1056 m->m_name); 1057 break; 1058 } 1059 pw = sm_getpwnam(q); 1060 if (pw == NULL) 1061 { 1062 syserr("readcf: mailer U= flag: unknown user %s", q); 1063 break; 1064 } 1065 else 1066 { 1067 m->m_uid = pw->pw_uid; 1068 m->m_gid = pw->pw_gid; 1069 } 1070 } 1071 else 1072 { 1073 auto char *q; 1074 1075 m->m_uid = strtol(p, &q, 0); 1076 p = q; 1077 while (isascii(*p) && isspace(*p)) 1078 p++; 1079 if (*p != '\0') 1080 p++; 1081 } 1082 while (isascii(*p) && isspace(*p)) 1083 p++; 1084 if (*p == '\0') 1085 break; 1086 if (isascii(*p) && !isdigit(*p)) 1087 { 1088 char *q = p; 1089 struct group *gr; 1090 1091 while (isascii(*p) && isalnum(*p)) 1092 p++; 1093 *p++ = '\0'; 1094 if (*q == '\0') 1095 { 1096 syserr("mailer %s: null group name", 1097 m->m_name); 1098 break; 1099 } 1100 gr = getgrnam(q); 1101 if (gr == NULL) 1102 { 1103 syserr("readcf: mailer U= flag: unknown group %s", q); 1104 break; 1105 } 1106 else 1107 m->m_gid = gr->gr_gid; 1108 } 1109 else 1110 { 1111 m->m_gid = strtol(p, NULL, 0); 1112 } 1113 break; 1114 1115 case 'W': /* wait timeout */ 1116 m->m_wait = convtime(p, 's'); 1117 break; 1118 1119 case '/': /* new root directory */ 1120 if (*p == '\0') 1121 syserr("mailer %s: null root directory", 1122 m->m_name); 1123 else 1124 m->m_rootdir = newstr(p); 1125 break; 1126 1127 default: 1128 syserr("M%s: unknown mailer equate %c=", 1129 m->m_name, fcode); 1130 break; 1131 } 1132 1133 p = delimptr; 1134 } 1135 1136 /* do some rationality checking */ 1137 if (m->m_argv == NULL) 1138 { 1139 syserr("M%s: A= argument required", m->m_name); 1140 return; 1141 } 1142 if (m->m_mailer == NULL) 1143 { 1144 syserr("M%s: P= argument required", m->m_name); 1145 return; 1146 } 1147 1148 if (NextMailer >= MAXMAILERS) 1149 { 1150 syserr("too many mailers defined (%d max)", MAXMAILERS); 1151 return; 1152 } 1153 1154 #if _FFR_DYNAMIC_TOBUF 1155 if (m->m_maxrcpt <= 0) 1156 m->m_maxrcpt = DEFAULT_MAX_RCPT; 1157 #endif /* _FFR_DYNAMIC_TOBUF */ 1158 1159 /* do some heuristic cleanup for back compatibility */ 1160 if (bitnset(M_LIMITS, m->m_flags)) 1161 { 1162 if (m->m_linelimit == 0) 1163 m->m_linelimit = SMTPLINELIM; 1164 if (ConfigLevel < 2) 1165 setbitn(M_7BITS, m->m_flags); 1166 } 1167 1168 if (strcmp(m->m_mailer, "[TCP]") == 0) 1169 { 1170 #if _FFR_REMOVE_TCP_MAILER_PATH 1171 syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n", 1172 m->m_name); 1173 return; 1174 #else /* _FFR_REMOVE_TCP_MAILER_PATH */ 1175 printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n", 1176 m->m_name); 1177 #endif /* _FFR_REMOVE_TCP_MAILER_PATH */ 1178 } 1179 1180 if (strcmp(m->m_mailer, "[IPC]") == 0 1181 #if !_FFR_REMOVE_TCP_MAILER_PATH 1182 || strcmp(m->m_mailer, "[TCP]") == 0 1183 #endif /* !_FFR_REMOVE_TCP_MAILER_PATH */ 1184 ) 1185 { 1186 /* Use the second argument for host or path to socket */ 1187 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1188 m->m_argv[1][0] == '\0') 1189 { 1190 syserr("M%s: too few parameters for %s mailer", 1191 m->m_name, m->m_mailer); 1192 return; 1193 } 1194 if (strcmp(m->m_argv[0], "TCP") != 0 1195 #if NETUNIX 1196 && strcmp(m->m_argv[0], "FILE") != 0 1197 #endif /* NETUNIX */ 1198 #if !_FFR_DEPRECATE_IPC_MAILER_ARG 1199 && strcmp(m->m_argv[0], "IPC") != 0 1200 #endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */ 1201 ) 1202 { 1203 printf("M%s: Warning: first argument in %s mailer must be %s\n", 1204 m->m_name, m->m_mailer, 1205 #if NETUNIX 1206 "TCP or FILE" 1207 #else /* NETUNIX */ 1208 "TCP" 1209 #endif /* NETUNIX */ 1210 ); 1211 } 1212 1213 } 1214 else if (strcmp(m->m_mailer, "[FILE]") == 0) 1215 { 1216 /* Use the second argument for filename */ 1217 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1218 m->m_argv[2] != NULL) 1219 { 1220 syserr("M%s: too %s parameters for [FILE] mailer", 1221 m->m_name, 1222 (m->m_argv[0] == NULL || 1223 m->m_argv[1] == NULL) ? "few" : "many"); 1224 return; 1225 } 1226 else if (strcmp(m->m_argv[0], "FILE") != 0) 1227 { 1228 syserr("M%s: first argument in [FILE] mailer must be FILE", 1229 m->m_name); 1230 return; 1231 } 1232 } 1233 1234 if (strcmp(m->m_mailer, "[IPC]") == 0 || 1235 strcmp(m->m_mailer, "[TCP]") == 0) 1236 { 1237 if (m->m_mtatype == NULL) 1238 m->m_mtatype = "dns"; 1239 if (m->m_addrtype == NULL) 1240 m->m_addrtype = "rfc822"; 1241 if (m->m_diagtype == NULL) 1242 { 1243 if (m->m_argv[0] != NULL && 1244 strcmp(m->m_argv[0], "FILE") == 0) 1245 m->m_diagtype = "x-unix"; 1246 else 1247 m->m_diagtype = "smtp"; 1248 } 1249 } 1250 1251 if (m->m_eol == NULL) 1252 { 1253 char **pp; 1254 1255 /* default for SMTP is \r\n; use \n for local delivery */ 1256 for (pp = m->m_argv; *pp != NULL; pp++) 1257 { 1258 for (p = *pp; *p != '\0'; ) 1259 { 1260 if ((*p++ & 0377) == MACROEXPAND && *p == 'u') 1261 break; 1262 } 1263 if (*p != '\0') 1264 break; 1265 } 1266 if (*pp == NULL) 1267 m->m_eol = "\r\n"; 1268 else 1269 m->m_eol = "\n"; 1270 } 1271 1272 /* enter the mailer into the symbol table */ 1273 s = stab(m->m_name, ST_MAILER, ST_ENTER); 1274 if (s->s_mailer != NULL) 1275 { 1276 i = s->s_mailer->m_mno; 1277 sm_free(s->s_mailer); 1278 } 1279 else 1280 { 1281 i = NextMailer++; 1282 } 1283 Mailer[i] = s->s_mailer = m; 1284 m->m_mno = i; 1285 } 1286 /* 1287 ** MUNCHSTRING -- translate a string into internal form. 1288 ** 1289 ** Parameters: 1290 ** p -- the string to munch. 1291 ** delimptr -- if non-NULL, set to the pointer of the 1292 ** field delimiter character. 1293 ** delim -- the delimiter for the field. 1294 ** 1295 ** Returns: 1296 ** the munched string. 1297 ** 1298 ** Side Effects: 1299 ** the munched string is a local static buffer. 1300 ** it must be copied before the function is called again. 1301 */ 1302 1303 char * 1304 munchstring(p, delimptr, delim) 1305 register char *p; 1306 char **delimptr; 1307 int delim; 1308 { 1309 register char *q; 1310 bool backslash = FALSE; 1311 bool quotemode = FALSE; 1312 static char buf[MAXLINE]; 1313 1314 for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) 1315 { 1316 if (backslash) 1317 { 1318 /* everything is roughly literal */ 1319 backslash = FALSE; 1320 switch (*p) 1321 { 1322 case 'r': /* carriage return */ 1323 *q++ = '\r'; 1324 continue; 1325 1326 case 'n': /* newline */ 1327 *q++ = '\n'; 1328 continue; 1329 1330 case 'f': /* form feed */ 1331 *q++ = '\f'; 1332 continue; 1333 1334 case 'b': /* backspace */ 1335 *q++ = '\b'; 1336 continue; 1337 } 1338 *q++ = *p; 1339 } 1340 else 1341 { 1342 if (*p == '\\') 1343 backslash = TRUE; 1344 else if (*p == '"') 1345 quotemode = !quotemode; 1346 else if (quotemode || *p != delim) 1347 *q++ = *p; 1348 else 1349 break; 1350 } 1351 } 1352 1353 if (delimptr != NULL) 1354 *delimptr = p; 1355 *q++ = '\0'; 1356 return buf; 1357 } 1358 /* 1359 ** MAKEARGV -- break up a string into words 1360 ** 1361 ** Parameters: 1362 ** p -- the string to break up. 1363 ** 1364 ** Returns: 1365 ** a char **argv (dynamically allocated) 1366 ** 1367 ** Side Effects: 1368 ** munges p. 1369 */ 1370 1371 static char ** 1372 makeargv(p) 1373 register char *p; 1374 { 1375 char *q; 1376 int i; 1377 char **avp; 1378 char *argv[MAXPV + 1]; 1379 1380 /* take apart the words */ 1381 i = 0; 1382 while (*p != '\0' && i < MAXPV) 1383 { 1384 q = p; 1385 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1386 p++; 1387 while (isascii(*p) && isspace(*p)) 1388 *p++ = '\0'; 1389 argv[i++] = newstr(q); 1390 } 1391 argv[i++] = NULL; 1392 1393 /* now make a copy of the argv */ 1394 avp = (char **) xalloc(sizeof *avp * i); 1395 memmove((char *) avp, (char *) argv, sizeof *avp * i); 1396 1397 return avp; 1398 } 1399 /* 1400 ** PRINTRULES -- print rewrite rules (for debugging) 1401 ** 1402 ** Parameters: 1403 ** none. 1404 ** 1405 ** Returns: 1406 ** none. 1407 ** 1408 ** Side Effects: 1409 ** prints rewrite rules. 1410 */ 1411 1412 void 1413 printrules() 1414 { 1415 register struct rewrite *rwp; 1416 register int ruleset; 1417 1418 for (ruleset = 0; ruleset < 10; ruleset++) 1419 { 1420 if (RewriteRules[ruleset] == NULL) 1421 continue; 1422 printf("\n----Rule Set %d:", ruleset); 1423 1424 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1425 { 1426 printf("\nLHS:"); 1427 printav(rwp->r_lhs); 1428 printf("RHS:"); 1429 printav(rwp->r_rhs); 1430 } 1431 } 1432 } 1433 /* 1434 ** PRINTMAILER -- print mailer structure (for debugging) 1435 ** 1436 ** Parameters: 1437 ** m -- the mailer to print 1438 ** 1439 ** Returns: 1440 ** none. 1441 */ 1442 1443 void 1444 printmailer(m) 1445 register MAILER *m; 1446 { 1447 int j; 1448 1449 printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer); 1450 if (RuleSetNames[m->m_se_rwset] == NULL) 1451 printf("%d/", m->m_se_rwset); 1452 else 1453 printf("%s/", RuleSetNames[m->m_se_rwset]); 1454 if (RuleSetNames[m->m_sh_rwset] == NULL) 1455 printf("%d R=", m->m_sh_rwset); 1456 else 1457 printf("%s R=", RuleSetNames[m->m_sh_rwset]); 1458 if (RuleSetNames[m->m_re_rwset] == NULL) 1459 printf("%d/", m->m_re_rwset); 1460 else 1461 printf("%s/", RuleSetNames[m->m_re_rwset]); 1462 if (RuleSetNames[m->m_rh_rwset] == NULL) 1463 printf("%d ", m->m_rh_rwset); 1464 else 1465 printf("%s ", RuleSetNames[m->m_rh_rwset]); 1466 printf("M=%ld U=%d:%d F=", m->m_maxsize, 1467 (int) m->m_uid, (int) m->m_gid); 1468 for (j = '\0'; j <= '\177'; j++) 1469 if (bitnset(j, m->m_flags)) 1470 (void) putchar(j); 1471 printf(" L=%d E=", m->m_linelimit); 1472 xputs(m->m_eol); 1473 if (m->m_defcharset != NULL) 1474 printf(" C=%s", m->m_defcharset); 1475 printf(" T=%s/%s/%s", 1476 m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, 1477 m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, 1478 m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); 1479 #if _FFR_DYNAMIC_TOBUF 1480 printf(" r=%d", m->m_maxrcpt); 1481 #endif /* _FFR_DYNAMIC_TOBUF */ 1482 if (m->m_argv != NULL) 1483 { 1484 char **a = m->m_argv; 1485 1486 printf(" A="); 1487 while (*a != NULL) 1488 { 1489 if (a != m->m_argv) 1490 printf(" "); 1491 xputs(*a++); 1492 } 1493 } 1494 printf("\n"); 1495 } 1496 /* 1497 ** SETOPTION -- set global processing option 1498 ** 1499 ** Parameters: 1500 ** opt -- option name. 1501 ** val -- option value (as a text string). 1502 ** safe -- set if this came from a configuration file. 1503 ** Some options (if set from the command line) will 1504 ** reset the user id to avoid security problems. 1505 ** sticky -- if set, don't let other setoptions override 1506 ** this value. 1507 ** e -- the main envelope. 1508 ** 1509 ** Returns: 1510 ** none. 1511 ** 1512 ** Side Effects: 1513 ** Sets options as implied by the arguments. 1514 */ 1515 1516 static BITMAP256 StickyOpt; /* set if option is stuck */ 1517 1518 #if NAMED_BIND 1519 1520 static struct resolverflags 1521 { 1522 char *rf_name; /* name of the flag */ 1523 long rf_bits; /* bits to set/clear */ 1524 } ResolverFlags[] = 1525 { 1526 { "debug", RES_DEBUG }, 1527 { "aaonly", RES_AAONLY }, 1528 { "usevc", RES_USEVC }, 1529 { "primary", RES_PRIMARY }, 1530 { "igntc", RES_IGNTC }, 1531 { "recurse", RES_RECURSE }, 1532 { "defnames", RES_DEFNAMES }, 1533 { "stayopen", RES_STAYOPEN }, 1534 { "dnsrch", RES_DNSRCH }, 1535 { "true", 0 }, /* avoid error on old syntax */ 1536 { NULL, 0 } 1537 }; 1538 1539 #endif /* NAMED_BIND */ 1540 1541 #define OI_NONE 0 /* no special treatment */ 1542 #define OI_SAFE 0x0001 /* safe for random people to use */ 1543 #define OI_SUBOPT 0x0002 /* option has suboptions */ 1544 1545 static struct optioninfo 1546 { 1547 char *o_name; /* long name of option */ 1548 u_char o_code; /* short name of option */ 1549 u_short o_flags; /* option flags */ 1550 } OptionTab[] = 1551 { 1552 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) 1553 { "RemoteMode", '>', OI_NONE }, 1554 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */ 1555 { "SevenBitInput", '7', OI_SAFE }, 1556 { "EightBitMode", '8', OI_SAFE }, 1557 { "AliasFile", 'A', OI_NONE }, 1558 { "AliasWait", 'a', OI_NONE }, 1559 { "BlankSub", 'B', OI_NONE }, 1560 { "MinFreeBlocks", 'b', OI_SAFE }, 1561 { "CheckpointInterval", 'C', OI_SAFE }, 1562 { "HoldExpensive", 'c', OI_NONE }, 1563 #if !_FFR_REMOVE_AUTOREBUILD 1564 { "AutoRebuildAliases", 'D', OI_NONE }, 1565 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 1566 { "DeliveryMode", 'd', OI_SAFE }, 1567 { "ErrorHeader", 'E', OI_NONE }, 1568 { "ErrorMode", 'e', OI_SAFE }, 1569 { "TempFileMode", 'F', OI_NONE }, 1570 { "SaveFromLine", 'f', OI_NONE }, 1571 { "MatchGECOS", 'G', OI_NONE }, 1572 { "HelpFile", 'H', OI_NONE }, 1573 { "MaxHopCount", 'h', OI_NONE }, 1574 { "ResolverOptions", 'I', OI_NONE }, 1575 { "IgnoreDots", 'i', OI_SAFE }, 1576 { "ForwardPath", 'J', OI_NONE }, 1577 { "SendMimeErrors", 'j', OI_SAFE }, 1578 { "ConnectionCacheSize", 'k', OI_NONE }, 1579 { "ConnectionCacheTimeout", 'K', OI_NONE }, 1580 { "UseErrorsTo", 'l', OI_NONE }, 1581 { "LogLevel", 'L', OI_SAFE }, 1582 { "MeToo", 'm', OI_SAFE }, 1583 { "CheckAliases", 'n', OI_NONE }, 1584 { "OldStyleHeaders", 'o', OI_SAFE }, 1585 { "DaemonPortOptions", 'O', OI_NONE }, 1586 { "PrivacyOptions", 'p', OI_SAFE }, 1587 { "PostmasterCopy", 'P', OI_NONE }, 1588 { "QueueFactor", 'q', OI_NONE }, 1589 { "QueueDirectory", 'Q', OI_NONE }, 1590 { "DontPruneRoutes", 'R', OI_NONE }, 1591 { "Timeout", 'r', OI_SUBOPT }, 1592 { "StatusFile", 'S', OI_NONE }, 1593 { "SuperSafe", 's', OI_SAFE }, 1594 { "QueueTimeout", 'T', OI_NONE }, 1595 { "TimeZoneSpec", 't', OI_NONE }, 1596 { "UserDatabaseSpec", 'U', OI_NONE }, 1597 { "DefaultUser", 'u', OI_NONE }, 1598 { "FallbackMXhost", 'V', OI_NONE }, 1599 { "Verbose", 'v', OI_SAFE }, 1600 { "TryNullMXList", 'w', OI_NONE }, 1601 { "QueueLA", 'x', OI_NONE }, 1602 { "RefuseLA", 'X', OI_NONE }, 1603 { "RecipientFactor", 'y', OI_NONE }, 1604 { "ForkEachJob", 'Y', OI_NONE }, 1605 { "ClassFactor", 'z', OI_NONE }, 1606 { "RetryFactor", 'Z', OI_NONE }, 1607 #define O_QUEUESORTORD 0x81 1608 { "QueueSortOrder", O_QUEUESORTORD, OI_SAFE }, 1609 #define O_HOSTSFILE 0x82 1610 { "HostsFile", O_HOSTSFILE, OI_NONE }, 1611 #define O_MQA 0x83 1612 { "MinQueueAge", O_MQA, OI_SAFE }, 1613 #define O_DEFCHARSET 0x85 1614 { "DefaultCharSet", O_DEFCHARSET, OI_SAFE }, 1615 #define O_SSFILE 0x86 1616 { "ServiceSwitchFile", O_SSFILE, OI_NONE }, 1617 #define O_DIALDELAY 0x87 1618 { "DialDelay", O_DIALDELAY, OI_SAFE }, 1619 #define O_NORCPTACTION 0x88 1620 { "NoRecipientAction", O_NORCPTACTION, OI_SAFE }, 1621 #define O_SAFEFILEENV 0x89 1622 { "SafeFileEnvironment", O_SAFEFILEENV, OI_NONE }, 1623 #define O_MAXMSGSIZE 0x8a 1624 { "MaxMessageSize", O_MAXMSGSIZE, OI_NONE }, 1625 #define O_COLONOKINADDR 0x8b 1626 { "ColonOkInAddr", O_COLONOKINADDR, OI_SAFE }, 1627 #define O_MAXQUEUERUN 0x8c 1628 { "MaxQueueRunSize", O_MAXQUEUERUN, OI_SAFE }, 1629 #define O_MAXCHILDREN 0x8d 1630 { "MaxDaemonChildren", O_MAXCHILDREN, OI_NONE }, 1631 #define O_KEEPCNAMES 0x8e 1632 { "DontExpandCnames", O_KEEPCNAMES, OI_NONE }, 1633 #define O_MUSTQUOTE 0x8f 1634 { "MustQuoteChars", O_MUSTQUOTE, OI_NONE }, 1635 #define O_SMTPGREETING 0x90 1636 { "SmtpGreetingMessage", O_SMTPGREETING, OI_NONE }, 1637 #define O_UNIXFROM 0x91 1638 { "UnixFromLine", O_UNIXFROM, OI_NONE }, 1639 #define O_OPCHARS 0x92 1640 { "OperatorChars", O_OPCHARS, OI_NONE }, 1641 #define O_DONTINITGRPS 0x93 1642 { "DontInitGroups", O_DONTINITGRPS, OI_NONE }, 1643 #define O_SLFH 0x94 1644 { "SingleLineFromHeader", O_SLFH, OI_SAFE }, 1645 #define O_ABH 0x95 1646 { "AllowBogusHELO", O_ABH, OI_SAFE }, 1647 #define O_CONNTHROT 0x97 1648 { "ConnectionRateThrottle", O_CONNTHROT, OI_NONE }, 1649 #define O_UGW 0x99 1650 { "UnsafeGroupWrites", O_UGW, OI_NONE }, 1651 #define O_DBLBOUNCE 0x9a 1652 { "DoubleBounceAddress", O_DBLBOUNCE, OI_NONE }, 1653 #define O_HSDIR 0x9b 1654 { "HostStatusDirectory", O_HSDIR, OI_NONE }, 1655 #define O_SINGTHREAD 0x9c 1656 { "SingleThreadDelivery", O_SINGTHREAD, OI_NONE }, 1657 #define O_RUNASUSER 0x9d 1658 { "RunAsUser", O_RUNASUSER, OI_NONE }, 1659 #define O_DSN_RRT 0x9e 1660 { "RrtImpliesDsn", O_DSN_RRT, OI_NONE }, 1661 #define O_PIDFILE 0x9f 1662 { "PidFile", O_PIDFILE, OI_NONE }, 1663 #define O_DONTBLAMESENDMAIL 0xa0 1664 { "DontBlameSendmail", O_DONTBLAMESENDMAIL, OI_NONE }, 1665 #define O_DPI 0xa1 1666 { "DontProbeInterfaces", O_DPI, OI_NONE }, 1667 #define O_MAXRCPT 0xa2 1668 { "MaxRecipientsPerMessage", O_MAXRCPT, OI_SAFE }, 1669 #define O_DEADLETTER 0xa3 1670 { "DeadLetterDrop", O_DEADLETTER, OI_NONE }, 1671 #if _FFR_DONTLOCKFILESFORREAD_OPTION 1672 # define O_DONTLOCK 0xa4 1673 { "DontLockFilesForRead", O_DONTLOCK, OI_NONE }, 1674 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 1675 #define O_MAXALIASRCSN 0xa5 1676 { "MaxAliasRecursion", O_MAXALIASRCSN, OI_NONE }, 1677 #define O_CNCTONLYTO 0xa6 1678 { "ConnectOnlyTo", O_CNCTONLYTO, OI_NONE }, 1679 #define O_TRUSTUSER 0xa7 1680 { "TrustedUser", O_TRUSTUSER, OI_NONE }, 1681 #define O_MAXMIMEHDRLEN 0xa8 1682 { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, OI_NONE }, 1683 #define O_CONTROLSOCKET 0xa9 1684 { "ControlSocketName", O_CONTROLSOCKET, OI_NONE }, 1685 #define O_MAXHDRSLEN 0xaa 1686 { "MaxHeadersLength", O_MAXHDRSLEN, OI_NONE }, 1687 #if _FFR_MAX_FORWARD_ENTRIES 1688 # define O_MAXFORWARD 0xab 1689 { "MaxForwardEntries", O_MAXFORWARD, OI_NONE }, 1690 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 1691 #define O_PROCTITLEPREFIX 0xac 1692 { "ProcessTitlePrefix", O_PROCTITLEPREFIX, OI_NONE }, 1693 #define O_SASLINFO 0xad 1694 #if _FFR_ALLOW_SASLINFO 1695 { "DefaultAuthInfo", O_SASLINFO, OI_SAFE }, 1696 #else /* _FFR_ALLOW_SASLINFO */ 1697 { "DefaultAuthInfo", O_SASLINFO, OI_NONE }, 1698 #endif /* _FFR_ALLOW_SASLINFO */ 1699 #define O_SASLMECH 0xae 1700 { "AuthMechanisms", O_SASLMECH, OI_NONE }, 1701 #define O_CLIENTPORT 0xaf 1702 { "ClientPortOptions", O_CLIENTPORT, OI_NONE }, 1703 #define O_DF_BUFSIZE 0xb0 1704 { "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE }, 1705 #define O_XF_BUFSIZE 0xb1 1706 { "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE }, 1707 # define O_LDAPDEFAULTSPEC 0xb2 1708 { "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE }, 1709 #if _FFR_QUEUEDELAY 1710 #define O_QUEUEDELAY 0xb3 1711 { "QueueDelay", O_QUEUEDELAY, OI_NONE }, 1712 #endif /* _FFR_QUEUEDELAY */ 1713 # define O_SRVCERTFILE 0xb4 1714 { "ServerCertFile", O_SRVCERTFILE, OI_NONE }, 1715 # define O_SRVKEYFILE 0xb5 1716 { "Serverkeyfile", O_SRVKEYFILE, OI_NONE }, 1717 # define O_CLTCERTFILE 0xb6 1718 { "ClientCertFile", O_CLTCERTFILE, OI_NONE }, 1719 # define O_CLTKEYFILE 0xb7 1720 { "Clientkeyfile", O_CLTKEYFILE, OI_NONE }, 1721 # define O_CACERTFILE 0xb8 1722 { "CACERTFile", O_CACERTFILE, OI_NONE }, 1723 # define O_CACERTPATH 0xb9 1724 { "CACERTPath", O_CACERTPATH, OI_NONE }, 1725 # define O_DHPARAMS 0xba 1726 { "DHParameters", O_DHPARAMS, OI_NONE }, 1727 #if _FFR_MILTER 1728 #define O_INPUTMILTER 0xbb 1729 { "InputMailFilters", O_INPUTMILTER, OI_NONE }, 1730 #define O_MILTER 0xbc 1731 { "Milter", O_MILTER, OI_SUBOPT }, 1732 #endif /* _FFR_MILTER */ 1733 #define O_SASLOPTS 0xbd 1734 { "AuthOptions", O_SASLOPTS, OI_NONE }, 1735 #if _FFR_QUEUE_FILE_MODE 1736 #define O_QUEUE_FILE_MODE 0xbe 1737 { "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE }, 1738 #endif /* _FFR_QUEUE_FILE_MODE */ 1739 # if _FFR_TLS_1 1740 # define O_DHPARAMS5 0xbf 1741 { "DHParameters512", O_DHPARAMS5, OI_NONE }, 1742 # define O_CIPHERLIST 0xc0 1743 { "CipherList", O_CIPHERLIST, OI_NONE }, 1744 # endif /* _FFR_TLS_1 */ 1745 # define O_RANDFILE 0xc1 1746 { "RandFile", O_RANDFILE, OI_NONE }, 1747 { NULL, '\0', OI_NONE } 1748 }; 1749 1750 void 1751 setoption(opt, val, safe, sticky, e) 1752 int opt; 1753 char *val; 1754 bool safe; 1755 bool sticky; 1756 register ENVELOPE *e; 1757 { 1758 register char *p; 1759 register struct optioninfo *o; 1760 char *subopt; 1761 int mid; 1762 bool can_setuid = RunAsUid == 0; 1763 auto char *ep; 1764 char buf[50]; 1765 extern bool Warn_Q_option; 1766 #if _FFR_ALLOW_SASLINFO 1767 extern int SubmitMode; 1768 #endif /* _FFR_ALLOW_SASLINFO */ 1769 1770 errno = 0; 1771 if (opt == ' ') 1772 { 1773 /* full word options */ 1774 struct optioninfo *sel; 1775 1776 p = strchr(val, '='); 1777 if (p == NULL) 1778 p = &val[strlen(val)]; 1779 while (*--p == ' ') 1780 continue; 1781 while (*++p == ' ') 1782 *p = '\0'; 1783 if (p == val) 1784 { 1785 syserr("readcf: null option name"); 1786 return; 1787 } 1788 if (*p == '=') 1789 *p++ = '\0'; 1790 while (*p == ' ') 1791 p++; 1792 subopt = strchr(val, '.'); 1793 if (subopt != NULL) 1794 *subopt++ = '\0'; 1795 sel = NULL; 1796 for (o = OptionTab; o->o_name != NULL; o++) 1797 { 1798 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1799 continue; 1800 if (strlen(o->o_name) == strlen(val)) 1801 { 1802 /* completely specified -- this must be it */ 1803 sel = NULL; 1804 break; 1805 } 1806 if (sel != NULL) 1807 break; 1808 sel = o; 1809 } 1810 if (sel != NULL && o->o_name == NULL) 1811 o = sel; 1812 else if (o->o_name == NULL) 1813 { 1814 syserr("readcf: unknown option name %s", val); 1815 return; 1816 } 1817 else if (sel != NULL) 1818 { 1819 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1820 val, sel->o_name, o->o_name); 1821 return; 1822 } 1823 if (strlen(val) != strlen(o->o_name)) 1824 { 1825 int oldVerbose = Verbose; 1826 1827 Verbose = 1; 1828 message("Option %s used as abbreviation for %s", 1829 val, o->o_name); 1830 Verbose = oldVerbose; 1831 } 1832 opt = o->o_code; 1833 val = p; 1834 } 1835 else 1836 { 1837 for (o = OptionTab; o->o_name != NULL; o++) 1838 { 1839 if (o->o_code == opt) 1840 break; 1841 } 1842 subopt = NULL; 1843 } 1844 1845 if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags)) 1846 { 1847 if (tTd(37, 1)) 1848 dprintf("setoption: %s does not support suboptions, ignoring .%s\n", 1849 o->o_name == NULL ? "<unknown>" : o->o_name, 1850 subopt); 1851 subopt = NULL; 1852 } 1853 1854 if (tTd(37, 1)) 1855 { 1856 dprintf(isascii(opt) && isprint(opt) ? 1857 "setoption %s (%c)%s%s=" : 1858 "setoption %s (0x%x)%s%s=", 1859 o->o_name == NULL ? "<unknown>" : o->o_name, 1860 opt, 1861 subopt == NULL ? "" : ".", 1862 subopt == NULL ? "" : subopt); 1863 xputs(val); 1864 } 1865 1866 /* 1867 ** See if this option is preset for us. 1868 */ 1869 1870 if (!sticky && bitnset(opt, StickyOpt)) 1871 { 1872 if (tTd(37, 1)) 1873 dprintf(" (ignored)\n"); 1874 return; 1875 } 1876 1877 /* 1878 ** Check to see if this option can be specified by this user. 1879 */ 1880 1881 if (!safe && RealUid == 0) 1882 safe = TRUE; 1883 if (!safe && !bitset(OI_SAFE, o->o_flags)) 1884 { 1885 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1886 { 1887 int dp; 1888 1889 if (tTd(37, 1)) 1890 dprintf(" (unsafe)"); 1891 dp = drop_privileges(TRUE); 1892 setstat(dp); 1893 } 1894 } 1895 if (tTd(37, 1)) 1896 dprintf("\n"); 1897 1898 switch (opt & 0xff) 1899 { 1900 case '7': /* force seven-bit input */ 1901 SevenBitInput = atobool(val); 1902 break; 1903 1904 case '8': /* handling of 8-bit input */ 1905 #if MIME8TO7 1906 switch (*val) 1907 { 1908 case 'm': /* convert 8-bit, convert MIME */ 1909 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1910 break; 1911 1912 case 'p': /* pass 8 bit, convert MIME */ 1913 MimeMode = MM_CVTMIME|MM_PASS8BIT; 1914 break; 1915 1916 case 's': /* strict adherence */ 1917 MimeMode = MM_CVTMIME; 1918 break; 1919 1920 # if 0 1921 case 'r': /* reject 8-bit, don't convert MIME */ 1922 MimeMode = 0; 1923 break; 1924 1925 case 'j': /* "just send 8" */ 1926 MimeMode = MM_PASS8BIT; 1927 break; 1928 1929 case 'a': /* encode 8 bit if available */ 1930 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1931 break; 1932 1933 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1934 MimeMode = MM_MIME8BIT; 1935 break; 1936 # endif /* 0 */ 1937 1938 default: 1939 syserr("Unknown 8-bit mode %c", *val); 1940 finis(FALSE, EX_USAGE); 1941 } 1942 #else /* MIME8TO7 */ 1943 printf("Warning: Option EightBitMode requires MIME8TO7 support\n"); 1944 #endif /* MIME8TO7 */ 1945 break; 1946 1947 case 'A': /* set default alias file */ 1948 if (val[0] == '\0') 1949 setalias("aliases"); 1950 else 1951 setalias(val); 1952 break; 1953 1954 case 'a': /* look N minutes for "@:@" in alias file */ 1955 if (val[0] == '\0') 1956 SafeAlias = 5 * 60; /* five minutes */ 1957 else 1958 SafeAlias = convtime(val, 'm'); 1959 break; 1960 1961 case 'B': /* substitution for blank character */ 1962 SpaceSub = val[0]; 1963 if (SpaceSub == '\0') 1964 SpaceSub = ' '; 1965 break; 1966 1967 case 'b': /* min blocks free on queue fs/max msg size */ 1968 p = strchr(val, '/'); 1969 if (p != NULL) 1970 { 1971 *p++ = '\0'; 1972 MaxMessageSize = atol(p); 1973 } 1974 MinBlocksFree = atol(val); 1975 break; 1976 1977 case 'c': /* don't connect to "expensive" mailers */ 1978 NoConnect = atobool(val); 1979 break; 1980 1981 case 'C': /* checkpoint every N addresses */ 1982 CheckpointInterval = atoi(val); 1983 break; 1984 1985 case 'd': /* delivery mode */ 1986 switch (*val) 1987 { 1988 case '\0': 1989 set_delivery_mode(SM_DELIVER, e); 1990 break; 1991 1992 case SM_QUEUE: /* queue only */ 1993 case SM_DEFER: /* queue only and defer map lookups */ 1994 #if !QUEUE 1995 syserr("need QUEUE to set -odqueue or -oddefer"); 1996 break; 1997 #endif /* !QUEUE */ 1998 /* FALLTHROUGH */ 1999 2000 case SM_DELIVER: /* do everything */ 2001 case SM_FORK: /* fork after verification */ 2002 set_delivery_mode(*val, e); 2003 break; 2004 2005 default: 2006 syserr("Unknown delivery mode %c", *val); 2007 finis(FALSE, EX_USAGE); 2008 } 2009 break; 2010 2011 #if !_FFR_REMOVE_AUTOREBUILD 2012 case 'D': /* rebuild alias database as needed */ 2013 AutoRebuild = atobool(val); 2014 break; 2015 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 2016 2017 case 'E': /* error message header/header file */ 2018 if (*val != '\0') 2019 ErrMsgFile = newstr(val); 2020 break; 2021 2022 case 'e': /* set error processing mode */ 2023 switch (*val) 2024 { 2025 case EM_QUIET: /* be silent about it */ 2026 case EM_MAIL: /* mail back */ 2027 case EM_BERKNET: /* do berknet error processing */ 2028 case EM_WRITE: /* write back (or mail) */ 2029 case EM_PRINT: /* print errors normally (default) */ 2030 e->e_errormode = *val; 2031 break; 2032 } 2033 break; 2034 2035 case 'F': /* file mode */ 2036 FileMode = atooct(val) & 0777; 2037 break; 2038 2039 case 'f': /* save Unix-style From lines on front */ 2040 SaveFrom = atobool(val); 2041 break; 2042 2043 case 'G': /* match recipients against GECOS field */ 2044 MatchGecos = atobool(val); 2045 break; 2046 2047 case 'g': /* default gid */ 2048 g_opt: 2049 if (isascii(*val) && isdigit(*val)) 2050 DefGid = atoi(val); 2051 else 2052 { 2053 register struct group *gr; 2054 2055 DefGid = -1; 2056 gr = getgrnam(val); 2057 if (gr == NULL) 2058 syserr("readcf: option %c: unknown group %s", 2059 opt, val); 2060 else 2061 DefGid = gr->gr_gid; 2062 } 2063 break; 2064 2065 case 'H': /* help file */ 2066 if (val[0] == '\0') 2067 HelpFile = "helpfile"; 2068 else 2069 { 2070 HelpFile = newstr(val); 2071 } 2072 break; 2073 2074 case 'h': /* maximum hop count */ 2075 MaxHopCount = atoi(val); 2076 break; 2077 2078 case 'I': /* use internet domain name server */ 2079 #if NAMED_BIND 2080 for (p = val; *p != 0; ) 2081 { 2082 bool clearmode; 2083 char *q; 2084 struct resolverflags *rfp; 2085 2086 while (*p == ' ') 2087 p++; 2088 if (*p == '\0') 2089 break; 2090 clearmode = FALSE; 2091 if (*p == '-') 2092 clearmode = TRUE; 2093 else if (*p != '+') 2094 p--; 2095 p++; 2096 q = p; 2097 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 2098 p++; 2099 if (*p != '\0') 2100 *p++ = '\0'; 2101 if (strcasecmp(q, "HasWildcardMX") == 0) 2102 { 2103 HasWildcardMX = !clearmode; 2104 continue; 2105 } 2106 #if _FFR_WORKAROUND_BROKEN_NAMESERVERS 2107 if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0) 2108 { 2109 WorkAroundBrokenAAAA = !clearmode; 2110 continue; 2111 } 2112 #endif /* _FFR_WORKAROUND_BROKEN_NAMESERVERS */ 2113 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 2114 { 2115 if (strcasecmp(q, rfp->rf_name) == 0) 2116 break; 2117 } 2118 if (rfp->rf_name == NULL) 2119 syserr("readcf: I option value %s unrecognized", q); 2120 else if (clearmode) 2121 _res.options &= ~rfp->rf_bits; 2122 else 2123 _res.options |= rfp->rf_bits; 2124 } 2125 if (tTd(8, 2)) 2126 dprintf("_res.options = %x, HasWildcardMX = %d\n", 2127 (u_int) _res.options, HasWildcardMX); 2128 #else /* NAMED_BIND */ 2129 usrerr("name server (I option) specified but BIND not compiled in"); 2130 #endif /* NAMED_BIND */ 2131 break; 2132 2133 case 'i': /* ignore dot lines in message */ 2134 IgnrDot = atobool(val); 2135 break; 2136 2137 case 'j': /* send errors in MIME (RFC 1341) format */ 2138 SendMIMEErrors = atobool(val); 2139 break; 2140 2141 case 'J': /* .forward search path */ 2142 ForwardPath = newstr(val); 2143 break; 2144 2145 case 'k': /* connection cache size */ 2146 MaxMciCache = atoi(val); 2147 if (MaxMciCache < 0) 2148 MaxMciCache = 0; 2149 break; 2150 2151 case 'K': /* connection cache timeout */ 2152 MciCacheTimeout = convtime(val, 'm'); 2153 break; 2154 2155 case 'l': /* use Errors-To: header */ 2156 UseErrorsTo = atobool(val); 2157 break; 2158 2159 case 'L': /* log level */ 2160 if (safe || LogLevel < atoi(val)) 2161 LogLevel = atoi(val); 2162 break; 2163 2164 case 'M': /* define macro */ 2165 sticky = FALSE; 2166 mid = macid(val, &ep); 2167 if (mid == 0) 2168 break; 2169 p = newstr(ep); 2170 if (!safe) 2171 cleanstrcpy(p, p, MAXNAME); 2172 define(mid, p, CurEnv); 2173 break; 2174 2175 case 'm': /* send to me too */ 2176 MeToo = atobool(val); 2177 break; 2178 2179 case 'n': /* validate RHS in newaliases */ 2180 CheckAliases = atobool(val); 2181 break; 2182 2183 /* 'N' available -- was "net name" */ 2184 2185 case 'O': /* daemon options */ 2186 #if DAEMON 2187 if (!setdaemonoptions(val)) 2188 syserr("too many daemons defined (%d max)", MAXDAEMONS); 2189 #else /* DAEMON */ 2190 syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); 2191 #endif /* DAEMON */ 2192 break; 2193 2194 case 'o': /* assume old style headers */ 2195 if (atobool(val)) 2196 CurEnv->e_flags |= EF_OLDSTYLE; 2197 else 2198 CurEnv->e_flags &= ~EF_OLDSTYLE; 2199 break; 2200 2201 case 'p': /* select privacy level */ 2202 p = val; 2203 for (;;) 2204 { 2205 register struct prival *pv; 2206 extern struct prival PrivacyValues[]; 2207 2208 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2209 p++; 2210 if (*p == '\0') 2211 break; 2212 val = p; 2213 while (isascii(*p) && isalnum(*p)) 2214 p++; 2215 if (*p != '\0') 2216 *p++ = '\0'; 2217 2218 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 2219 { 2220 if (strcasecmp(val, pv->pv_name) == 0) 2221 break; 2222 } 2223 if (pv->pv_name == NULL) 2224 syserr("readcf: Op line: %s unrecognized", val); 2225 else 2226 PrivacyFlags |= pv->pv_flag; 2227 } 2228 sticky = FALSE; 2229 break; 2230 2231 case 'P': /* postmaster copy address for returned mail */ 2232 PostMasterCopy = newstr(val); 2233 break; 2234 2235 case 'q': /* slope of queue only function */ 2236 QueueFactor = atoi(val); 2237 break; 2238 2239 case 'Q': /* queue directory */ 2240 if (val[0] == '\0') 2241 { 2242 QueueDir = "mqueue"; 2243 } 2244 else 2245 { 2246 QueueDir = newstr(val); 2247 } 2248 if (RealUid != 0 && !safe) 2249 Warn_Q_option = TRUE; 2250 break; 2251 2252 case 'R': /* don't prune routes */ 2253 DontPruneRoutes = atobool(val); 2254 break; 2255 2256 case 'r': /* read timeout */ 2257 if (subopt == NULL) 2258 inittimeouts(val, sticky); 2259 else 2260 settimeout(subopt, val, sticky); 2261 break; 2262 2263 case 'S': /* status file */ 2264 if (val[0] == '\0') 2265 StatFile = "statistics"; 2266 else 2267 { 2268 StatFile = newstr(val); 2269 } 2270 break; 2271 2272 case 's': /* be super safe, even if expensive */ 2273 SuperSafe = atobool(val); 2274 break; 2275 2276 case 'T': /* queue timeout */ 2277 p = strchr(val, '/'); 2278 if (p != NULL) 2279 { 2280 *p++ = '\0'; 2281 settimeout("queuewarn", p, sticky); 2282 } 2283 settimeout("queuereturn", val, sticky); 2284 break; 2285 2286 case 't': /* time zone name */ 2287 TimeZoneSpec = newstr(val); 2288 break; 2289 2290 case 'U': /* location of user database */ 2291 UdbSpec = newstr(val); 2292 break; 2293 2294 case 'u': /* set default uid */ 2295 for (p = val; *p != '\0'; p++) 2296 { 2297 if (*p == '.' || *p == '/' || *p == ':') 2298 { 2299 *p++ = '\0'; 2300 break; 2301 } 2302 } 2303 if (isascii(*val) && isdigit(*val)) 2304 { 2305 DefUid = atoi(val); 2306 setdefuser(); 2307 } 2308 else 2309 { 2310 register struct passwd *pw; 2311 2312 DefUid = -1; 2313 pw = sm_getpwnam(val); 2314 if (pw == NULL) 2315 { 2316 syserr("readcf: option u: unknown user %s", val); 2317 break; 2318 } 2319 else 2320 { 2321 DefUid = pw->pw_uid; 2322 DefGid = pw->pw_gid; 2323 DefUser = newstr(pw->pw_name); 2324 } 2325 } 2326 2327 #ifdef UID_MAX 2328 if (DefUid > UID_MAX) 2329 { 2330 syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", 2331 (long) DefUid, (long) UID_MAX); 2332 break; 2333 } 2334 #endif /* UID_MAX */ 2335 2336 /* handle the group if it is there */ 2337 if (*p == '\0') 2338 break; 2339 val = p; 2340 goto g_opt; 2341 2342 case 'V': /* fallback MX host */ 2343 if (val[0] != '\0') 2344 FallBackMX = newstr(val); 2345 break; 2346 2347 case 'v': /* run in verbose mode */ 2348 Verbose = atobool(val) ? 1 : 0; 2349 break; 2350 2351 case 'w': /* if we are best MX, try host directly */ 2352 TryNullMXList = atobool(val); 2353 break; 2354 2355 /* 'W' available -- was wizard password */ 2356 2357 case 'x': /* load avg at which to auto-queue msgs */ 2358 QueueLA = atoi(val); 2359 break; 2360 2361 case 'X': /* load avg at which to auto-reject connections */ 2362 RefuseLA = atoi(val); 2363 break; 2364 2365 case 'y': /* work recipient factor */ 2366 WkRecipFact = atoi(val); 2367 break; 2368 2369 case 'Y': /* fork jobs during queue runs */ 2370 ForkQueueRuns = atobool(val); 2371 break; 2372 2373 case 'z': /* work message class factor */ 2374 WkClassFact = atoi(val); 2375 break; 2376 2377 case 'Z': /* work time factor */ 2378 WkTimeFact = atoi(val); 2379 break; 2380 2381 2382 case O_QUEUESORTORD: /* queue sorting order */ 2383 switch (*val) 2384 { 2385 case 'h': /* Host first */ 2386 case 'H': 2387 QueueSortOrder = QSO_BYHOST; 2388 break; 2389 2390 case 'p': /* Priority order */ 2391 case 'P': 2392 QueueSortOrder = QSO_BYPRIORITY; 2393 break; 2394 2395 case 't': /* Submission time */ 2396 case 'T': 2397 QueueSortOrder = QSO_BYTIME; 2398 break; 2399 2400 case 'f': /* File Name */ 2401 case 'F': 2402 QueueSortOrder = QSO_BYFILENAME; 2403 break; 2404 2405 default: 2406 syserr("Invalid queue sort order \"%s\"", val); 2407 } 2408 break; 2409 2410 #if _FFR_QUEUEDELAY 2411 case O_QUEUEDELAY: /* queue delay algorithm */ 2412 switch (*val) 2413 { 2414 case 'e': /* exponential */ 2415 case 'E': 2416 QueueAlg = QD_EXP; 2417 QueueInitDelay = 10 MINUTES; 2418 QueueMaxDelay = 2 HOURS; 2419 p = strchr(val, '/'); 2420 if (p != NULL) 2421 { 2422 char *q; 2423 2424 *p++ = '\0'; 2425 q = strchr(p, '/'); 2426 if (q != NULL) 2427 *q++ = '\0'; 2428 QueueInitDelay = convtime(p, 's'); 2429 if (q != NULL) 2430 { 2431 QueueMaxDelay = convtime(q, 's'); 2432 } 2433 } 2434 break; 2435 2436 case 'l': /* linear */ 2437 case 'L': 2438 QueueAlg = QD_LINEAR; 2439 break; 2440 2441 default: 2442 syserr("Invalid queue delay algorithm \"%s\"", val); 2443 } 2444 break; 2445 #endif /* _FFR_QUEUEDELAY */ 2446 2447 case O_HOSTSFILE: /* pathname of /etc/hosts file */ 2448 HostsFile = newstr(val); 2449 break; 2450 2451 case O_MQA: /* minimum queue age between deliveries */ 2452 MinQueueAge = convtime(val, 'm'); 2453 break; 2454 2455 case O_DEFCHARSET: /* default character set for mimefying */ 2456 DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 2457 break; 2458 2459 case O_SSFILE: /* service switch file */ 2460 ServiceSwitchFile = newstr(val); 2461 break; 2462 2463 case O_DIALDELAY: /* delay for dial-on-demand operation */ 2464 DialDelay = convtime(val, 's'); 2465 break; 2466 2467 case O_NORCPTACTION: /* what to do if no recipient */ 2468 if (strcasecmp(val, "none") == 0) 2469 NoRecipientAction = NRA_NO_ACTION; 2470 else if (strcasecmp(val, "add-to") == 0) 2471 NoRecipientAction = NRA_ADD_TO; 2472 else if (strcasecmp(val, "add-apparently-to") == 0) 2473 NoRecipientAction = NRA_ADD_APPARENTLY_TO; 2474 else if (strcasecmp(val, "add-bcc") == 0) 2475 NoRecipientAction = NRA_ADD_BCC; 2476 else if (strcasecmp(val, "add-to-undisclosed") == 0) 2477 NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 2478 else 2479 syserr("Invalid NoRecipientAction: %s", val); 2480 break; 2481 2482 case O_SAFEFILEENV: /* chroot() environ for writing to files */ 2483 SafeFileEnv = newstr(val); 2484 break; 2485 2486 case O_MAXMSGSIZE: /* maximum message size */ 2487 MaxMessageSize = atol(val); 2488 break; 2489 2490 case O_COLONOKINADDR: /* old style handling of colon addresses */ 2491 ColonOkInAddr = atobool(val); 2492 break; 2493 2494 case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 2495 MaxQueueRun = atol(val); 2496 break; 2497 2498 case O_MAXCHILDREN: /* max # of children of daemon */ 2499 MaxChildren = atoi(val); 2500 break; 2501 2502 #if _FFR_MAX_FORWARD_ENTRIES 2503 case O_MAXFORWARD: /* max # of forward entries */ 2504 MaxForwardEntries = atoi(val); 2505 break; 2506 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 2507 2508 case O_KEEPCNAMES: /* don't expand CNAME records */ 2509 DontExpandCnames = atobool(val); 2510 break; 2511 2512 case O_MUSTQUOTE: /* must quote these characters in phrases */ 2513 (void) strlcpy(buf, "@,;:\\()[]", sizeof buf); 2514 if (strlen(val) < (SIZE_T) sizeof buf - 10) 2515 (void) strlcat(buf, val, sizeof buf); 2516 else 2517 printf("Warning: MustQuoteChars too long, ignored.\n"); 2518 MustQuoteChars = newstr(buf); 2519 break; 2520 2521 case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ 2522 SmtpGreeting = newstr(munchstring(val, NULL, '\0')); 2523 break; 2524 2525 case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ 2526 UnixFromLine = newstr(munchstring(val, NULL, '\0')); 2527 break; 2528 2529 case O_OPCHARS: /* operator characters (old $o macro) */ 2530 if (OperatorChars != NULL) 2531 printf("Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n"); 2532 OperatorChars = newstr(munchstring(val, NULL, '\0')); 2533 break; 2534 2535 case O_DONTINITGRPS: /* don't call initgroups(3) */ 2536 DontInitGroups = atobool(val); 2537 break; 2538 2539 case O_SLFH: /* make sure from fits on one line */ 2540 SingleLineFromHeader = atobool(val); 2541 break; 2542 2543 case O_ABH: /* allow HELO commands with syntax errors */ 2544 AllowBogusHELO = atobool(val); 2545 break; 2546 2547 case O_CONNTHROT: /* connection rate throttle */ 2548 ConnRateThrottle = atoi(val); 2549 break; 2550 2551 case O_UGW: /* group writable files are unsafe */ 2552 if (!atobool(val)) 2553 { 2554 setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE, 2555 DontBlameSendmail); 2556 setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE, 2557 DontBlameSendmail); 2558 } 2559 break; 2560 2561 case O_DBLBOUNCE: /* address to which to send double bounces */ 2562 if (val[0] != '\0') 2563 DoubleBounceAddr = newstr(val); 2564 else 2565 syserr("readcf: option DoubleBounceAddress: value required"); 2566 break; 2567 2568 case O_HSDIR: /* persistent host status directory */ 2569 if (val[0] != '\0') 2570 { 2571 HostStatDir = newstr(val); 2572 } 2573 break; 2574 2575 case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ 2576 SingleThreadDelivery = atobool(val); 2577 break; 2578 2579 case O_RUNASUSER: /* run bulk of code as this user */ 2580 for (p = val; *p != '\0'; p++) 2581 { 2582 if (*p == '.' || *p == '/' || *p == ':') 2583 { 2584 *p++ = '\0'; 2585 break; 2586 } 2587 } 2588 if (isascii(*val) && isdigit(*val)) 2589 { 2590 if (can_setuid) 2591 RunAsUid = atoi(val); 2592 } 2593 else 2594 { 2595 register struct passwd *pw; 2596 2597 pw = sm_getpwnam(val); 2598 if (pw == NULL) 2599 { 2600 syserr("readcf: option RunAsUser: unknown user %s", val); 2601 break; 2602 } 2603 else if (can_setuid) 2604 { 2605 if (*p == '\0') 2606 RunAsUserName = newstr(val); 2607 RunAsUid = pw->pw_uid; 2608 RunAsGid = pw->pw_gid; 2609 } 2610 } 2611 #ifdef UID_MAX 2612 if (RunAsUid > UID_MAX) 2613 { 2614 syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", 2615 (long) RunAsUid, (long) UID_MAX); 2616 break; 2617 } 2618 #endif /* UID_MAX */ 2619 if (*p != '\0') 2620 { 2621 if (isascii(*p) && isdigit(*p)) 2622 { 2623 if (can_setuid) 2624 RunAsGid = atoi(p); 2625 } 2626 else 2627 { 2628 register struct group *gr; 2629 2630 gr = getgrnam(p); 2631 if (gr == NULL) 2632 syserr("readcf: option RunAsUser: unknown group %s", 2633 p); 2634 else if (can_setuid) 2635 RunAsGid = gr->gr_gid; 2636 } 2637 } 2638 if (tTd(47, 5)) 2639 dprintf("readcf: RunAsUser = %d:%d\n", 2640 (int)RunAsUid, (int)RunAsGid); 2641 break; 2642 2643 case O_DSN_RRT: 2644 RrtImpliesDsn = atobool(val); 2645 break; 2646 2647 case O_PIDFILE: 2648 if (PidFile != NULL) 2649 sm_free(PidFile); 2650 PidFile = newstr(val); 2651 break; 2652 2653 case O_DONTBLAMESENDMAIL: 2654 p = val; 2655 for (;;) 2656 { 2657 register struct dbsval *dbs; 2658 extern struct dbsval DontBlameSendmailValues[]; 2659 2660 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2661 p++; 2662 if (*p == '\0') 2663 break; 2664 val = p; 2665 while (isascii(*p) && isalnum(*p)) 2666 p++; 2667 if (*p != '\0') 2668 *p++ = '\0'; 2669 2670 for (dbs = DontBlameSendmailValues; 2671 dbs->dbs_name != NULL; dbs++) 2672 { 2673 if (strcasecmp(val, dbs->dbs_name) == 0) 2674 break; 2675 } 2676 if (dbs->dbs_name == NULL) 2677 syserr("readcf: DontBlameSendmail option: %s unrecognized", val); 2678 else if (dbs->dbs_flag == DBS_SAFE) 2679 clrbitmap(DontBlameSendmail); 2680 else 2681 setbitn(dbs->dbs_flag, DontBlameSendmail); 2682 } 2683 sticky = FALSE; 2684 break; 2685 2686 case O_DPI: 2687 DontProbeInterfaces = atobool(val); 2688 break; 2689 2690 case O_MAXRCPT: 2691 MaxRcptPerMsg = atoi(val); 2692 break; 2693 2694 case O_DEADLETTER: 2695 if (DeadLetterDrop != NULL) 2696 sm_free(DeadLetterDrop); 2697 DeadLetterDrop = newstr(val); 2698 break; 2699 2700 #if _FFR_DONTLOCKFILESFORREAD_OPTION 2701 case O_DONTLOCK: 2702 DontLockReadFiles = atobool(val); 2703 break; 2704 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 2705 2706 case O_MAXALIASRCSN: 2707 MaxAliasRecursion = atoi(val); 2708 break; 2709 2710 case O_CNCTONLYTO: 2711 /* XXX should probably use gethostbyname */ 2712 #if NETINET || NETINET6 2713 # if NETINET6 2714 if (inet_addr(val) == INADDR_NONE) 2715 { 2716 ConnectOnlyTo.sa.sa_family = AF_INET6; 2717 if (inet_pton(AF_INET6, val, 2718 &ConnectOnlyTo.sin6.sin6_addr) != 1) 2719 syserr("readcf: option ConnectOnlyTo: invalid IP address %s", 2720 val); 2721 } 2722 else 2723 # endif /* NETINET6 */ 2724 { 2725 ConnectOnlyTo.sa.sa_family = AF_INET; 2726 ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val); 2727 } 2728 #endif /* NETINET || NETINET6 */ 2729 break; 2730 2731 case O_TRUSTUSER: 2732 #if HASFCHOWN 2733 if (isascii(*val) && isdigit(*val)) 2734 TrustedUid = atoi(val); 2735 else 2736 { 2737 register struct passwd *pw; 2738 2739 TrustedUid = 0; 2740 pw = sm_getpwnam(val); 2741 if (pw == NULL) 2742 { 2743 syserr("readcf: option TrustedUser: unknown user %s", val); 2744 break; 2745 } 2746 else 2747 TrustedUid = pw->pw_uid; 2748 } 2749 2750 # ifdef UID_MAX 2751 if (TrustedUid > UID_MAX) 2752 { 2753 syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)", 2754 (long) TrustedUid, (long) UID_MAX); 2755 TrustedUid = 0; 2756 } 2757 # endif /* UID_MAX */ 2758 #else /* HASFCHOWN */ 2759 syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()"); 2760 #endif /* HASFCHOWN */ 2761 break; 2762 2763 case O_MAXMIMEHDRLEN: 2764 p = strchr(val, '/'); 2765 if (p != NULL) 2766 *p++ = '\0'; 2767 MaxMimeHeaderLength = atoi(val); 2768 if (p != NULL && *p != '\0') 2769 MaxMimeFieldLength = atoi(p); 2770 else 2771 MaxMimeFieldLength = MaxMimeHeaderLength / 2; 2772 2773 if (MaxMimeHeaderLength < 0) 2774 MaxMimeHeaderLength = 0; 2775 else if (MaxMimeHeaderLength < 128) 2776 printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); 2777 2778 if (MaxMimeFieldLength < 0) 2779 MaxMimeFieldLength = 0; 2780 else if (MaxMimeFieldLength < 40) 2781 printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); 2782 break; 2783 2784 case O_CONTROLSOCKET: 2785 if (ControlSocketName != NULL) 2786 sm_free(ControlSocketName); 2787 ControlSocketName = newstr(val); 2788 break; 2789 2790 case O_MAXHDRSLEN: 2791 MaxHeadersLength = atoi(val); 2792 2793 if (MaxHeadersLength > 0 && 2794 MaxHeadersLength < (MAXHDRSLEN / 2)) 2795 printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2)); 2796 break; 2797 2798 case O_PROCTITLEPREFIX: 2799 if (ProcTitlePrefix != NULL) 2800 sm_free(ProcTitlePrefix); 2801 ProcTitlePrefix = newstr(val); 2802 break; 2803 2804 #if SASL 2805 case O_SASLINFO: 2806 #if _FFR_ALLOW_SASLINFO 2807 /* 2808 ** Allow users to select their own authinfo file. 2809 ** However, this is not a "perfect" solution. 2810 ** If mail is queued, the authentication info 2811 ** will not be used in subsequent delivery attempts. 2812 ** If we really want to support this, then it has 2813 ** to be stored in the queue file. 2814 */ 2815 if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 && 2816 RunAsUid != RealUid) 2817 { 2818 errno = 0; 2819 syserr("Error: %s only allowed with -U\n", 2820 o->o_name == NULL ? "<unknown>" : o->o_name); 2821 ExitStat = EX_USAGE; 2822 break; 2823 } 2824 #endif /* _FFR_ALLOW_SASLINFO */ 2825 if (SASLInfo != NULL) 2826 sm_free(SASLInfo); 2827 SASLInfo = newstr(val); 2828 break; 2829 2830 case O_SASLMECH: 2831 if (AuthMechanisms != NULL) 2832 sm_free(AuthMechanisms); 2833 if (*val != '\0') 2834 AuthMechanisms = newstr(val); 2835 else 2836 AuthMechanisms = NULL; 2837 break; 2838 2839 case O_SASLOPTS: 2840 while (val != NULL && *val != '\0') 2841 { 2842 switch(*val) 2843 { 2844 case 'A': 2845 SASLOpts |= SASL_AUTH_AUTH; 2846 break; 2847 # if _FFR_SASL_OPTS 2848 case 'a': 2849 SASLOpts |= SASL_SEC_NOACTIVE; 2850 break; 2851 case 'c': 2852 SASLOpts |= SASL_SEC_PASS_CREDENTIALS; 2853 break; 2854 case 'd': 2855 SASLOpts |= SASL_SEC_NODICTIONARY; 2856 break; 2857 case 'f': 2858 SASLOpts |= SASL_SEC_FORWARD_SECRECY; 2859 break; 2860 case 'p': 2861 SASLOpts |= SASL_SEC_NOPLAINTEXT; 2862 break; 2863 case 'y': 2864 SASLOpts |= SASL_SEC_NOANONYMOUS; 2865 break; 2866 # endif /* _FFR_SASL_OPTS */ 2867 default: 2868 printf("Warning: Option: %s unknown parameter '%c'\n", 2869 o->o_name == NULL ? "<unknown>" 2870 : o->o_name, 2871 (isascii(*val) && isprint(*val)) ? *val 2872 : '?'); 2873 break; 2874 } 2875 ++val; 2876 val = strpbrk(val, ", \t"); 2877 if (val != NULL) 2878 ++val; 2879 } 2880 break; 2881 2882 #else /* SASL */ 2883 case O_SASLINFO: 2884 case O_SASLMECH: 2885 case O_SASLOPTS: 2886 printf("Warning: Option: %s requires SASL support (-DSASL)\n", 2887 o->o_name == NULL ? "<unknown>" : o->o_name); 2888 break; 2889 #endif /* SASL */ 2890 2891 #if STARTTLS 2892 case O_SRVCERTFILE: 2893 if (SrvCERTfile != NULL) 2894 sm_free(SrvCERTfile); 2895 SrvCERTfile = newstr(val); 2896 break; 2897 2898 case O_SRVKEYFILE: 2899 if (Srvkeyfile != NULL) 2900 sm_free(Srvkeyfile); 2901 Srvkeyfile = newstr(val); 2902 break; 2903 2904 case O_CLTCERTFILE: 2905 if (CltCERTfile != NULL) 2906 sm_free(CltCERTfile); 2907 CltCERTfile = newstr(val); 2908 break; 2909 2910 case O_CLTKEYFILE: 2911 if (Cltkeyfile != NULL) 2912 sm_free(Cltkeyfile); 2913 Cltkeyfile = newstr(val); 2914 break; 2915 2916 case O_CACERTFILE: 2917 if (CACERTfile != NULL) 2918 sm_free(CACERTfile); 2919 CACERTfile = newstr(val); 2920 break; 2921 2922 case O_CACERTPATH: 2923 if (CACERTpath != NULL) 2924 sm_free(CACERTpath); 2925 CACERTpath = newstr(val); 2926 break; 2927 2928 case O_DHPARAMS: 2929 if (DHParams != NULL) 2930 sm_free(DHParams); 2931 DHParams = newstr(val); 2932 break; 2933 2934 # if _FFR_TLS_1 2935 case O_DHPARAMS5: 2936 if (DHParams5 != NULL) 2937 sm_free(DHParams5); 2938 DHParams5 = newstr(val); 2939 break; 2940 2941 case O_CIPHERLIST: 2942 if (CipherList != NULL) 2943 sm_free(CipherList); 2944 CipherList = newstr(val); 2945 break; 2946 # endif /* _FFR_TLS_1 */ 2947 2948 case O_RANDFILE: 2949 if (RandFile != NULL) 2950 sm_free(RandFile); 2951 RandFile= newstr(val); 2952 break; 2953 2954 # else /* STARTTLS */ 2955 case O_SRVCERTFILE: 2956 case O_SRVKEYFILE: 2957 case O_CLTCERTFILE: 2958 case O_CLTKEYFILE: 2959 case O_CACERTFILE: 2960 case O_CACERTPATH: 2961 case O_DHPARAMS: 2962 # if _FFR_TLS_1 2963 case O_DHPARAMS5: 2964 case O_CIPHERLIST: 2965 # endif /* _FFR_TLS_1 */ 2966 case O_RANDFILE: 2967 printf("Warning: Option: %s requires TLS support\n", 2968 o->o_name == NULL ? "<unknown>" : o->o_name); 2969 break; 2970 2971 # endif /* STARTTLS */ 2972 2973 case O_CLIENTPORT: 2974 #if DAEMON 2975 setclientoptions(val); 2976 #else /* DAEMON */ 2977 syserr("ClientPortOptions (O option) set but DAEMON not compiled in"); 2978 #endif /* DAEMON */ 2979 break; 2980 2981 case O_DF_BUFSIZE: 2982 DataFileBufferSize = atoi(val); 2983 break; 2984 2985 case O_XF_BUFSIZE: 2986 XscriptFileBufferSize = atoi(val); 2987 break; 2988 2989 case O_LDAPDEFAULTSPEC: 2990 #ifdef LDAPMAP 2991 ldapmap_set_defaults(val); 2992 #else /* LDAPMAP */ 2993 printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n", 2994 o->o_name == NULL ? "<unknown>" : o->o_name); 2995 #endif /* LDAPMAP */ 2996 break; 2997 2998 #if _FFR_MILTER 2999 case O_INPUTMILTER: 3000 InputFilterList = newstr(val); 3001 break; 3002 3003 case O_MILTER: 3004 milter_set_option(subopt, val, sticky); 3005 break; 3006 #endif /* _FFR_MILTER */ 3007 3008 #if _FFR_QUEUE_FILE_MODE 3009 case O_QUEUE_FILE_MODE: /* queue file mode */ 3010 QueueFileMode = atooct(val) & 0777; 3011 break; 3012 #endif /* _FFR_QUEUE_FILE_MODE */ 3013 3014 default: 3015 if (tTd(37, 1)) 3016 { 3017 if (isascii(opt) && isprint(opt)) 3018 dprintf("Warning: option %c unknown\n", opt); 3019 else 3020 dprintf("Warning: option 0x%x unknown\n", opt); 3021 } 3022 break; 3023 } 3024 3025 /* 3026 ** Options with suboptions are responsible for taking care 3027 ** of sticky-ness (e.g., that a command line setting is kept 3028 ** when reading in the sendmail.cf file). This has to be done 3029 ** when the suboptions are parsed since each suboption must be 3030 ** sticky, not the root option. 3031 */ 3032 3033 if (sticky && !bitset(OI_SUBOPT, o->o_flags)) 3034 setbitn(opt, StickyOpt); 3035 } 3036 /* 3037 ** SETCLASS -- set a string into a class 3038 ** 3039 ** Parameters: 3040 ** class -- the class to put the string in. 3041 ** str -- the string to enter 3042 ** 3043 ** Returns: 3044 ** none. 3045 ** 3046 ** Side Effects: 3047 ** puts the word into the symbol table. 3048 */ 3049 3050 void 3051 setclass(class, str) 3052 int class; 3053 char *str; 3054 { 3055 register STAB *s; 3056 3057 if ((*str & 0377) == MATCHCLASS) 3058 { 3059 int mid; 3060 3061 str++; 3062 mid = macid(str, NULL); 3063 if (mid == 0) 3064 return; 3065 3066 if (tTd(37, 8)) 3067 dprintf("setclass(%s, $=%s)\n", 3068 macname(class), macname(mid)); 3069 copy_class(mid, class); 3070 } 3071 else 3072 { 3073 if (tTd(37, 8)) 3074 dprintf("setclass(%s, %s)\n", macname(class), str); 3075 3076 s = stab(str, ST_CLASS, ST_ENTER); 3077 setbitn(bitidx(class), s->s_class); 3078 } 3079 } 3080 /* 3081 ** MAKEMAPENTRY -- create a map entry 3082 ** 3083 ** Parameters: 3084 ** line -- the config file line 3085 ** 3086 ** Returns: 3087 ** A pointer to the map that has been created. 3088 ** NULL if there was a syntax error. 3089 ** 3090 ** Side Effects: 3091 ** Enters the map into the dictionary. 3092 */ 3093 3094 MAP * 3095 makemapentry(line) 3096 char *line; 3097 { 3098 register char *p; 3099 char *mapname; 3100 char *classname; 3101 register STAB *s; 3102 STAB *class; 3103 3104 for (p = line; isascii(*p) && isspace(*p); p++) 3105 continue; 3106 if (!(isascii(*p) && isalnum(*p))) 3107 { 3108 syserr("readcf: config K line: no map name"); 3109 return NULL; 3110 } 3111 3112 mapname = p; 3113 while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') 3114 continue; 3115 if (*p != '\0') 3116 *p++ = '\0'; 3117 while (isascii(*p) && isspace(*p)) 3118 p++; 3119 if (!(isascii(*p) && isalnum(*p))) 3120 { 3121 syserr("readcf: config K line, map %s: no map class", mapname); 3122 return NULL; 3123 } 3124 classname = p; 3125 while (isascii(*++p) && isalnum(*p)) 3126 continue; 3127 if (*p != '\0') 3128 *p++ = '\0'; 3129 while (isascii(*p) && isspace(*p)) 3130 p++; 3131 3132 /* look up the class */ 3133 class = stab(classname, ST_MAPCLASS, ST_FIND); 3134 if (class == NULL) 3135 { 3136 syserr("readcf: map %s: class %s not available", mapname, classname); 3137 return NULL; 3138 } 3139 3140 /* enter the map */ 3141 s = stab(mapname, ST_MAP, ST_ENTER); 3142 s->s_map.map_class = &class->s_mapclass; 3143 s->s_map.map_mname = newstr(mapname); 3144 3145 if (class->s_mapclass.map_parse(&s->s_map, p)) 3146 s->s_map.map_mflags |= MF_VALID; 3147 3148 if (tTd(37, 5)) 3149 { 3150 dprintf("map %s, class %s, flags %lx, file %s,\n", 3151 s->s_map.map_mname, s->s_map.map_class->map_cname, 3152 s->s_map.map_mflags, 3153 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 3154 dprintf("\tapp %s, domain %s, rebuild %s\n", 3155 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 3156 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 3157 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 3158 } 3159 3160 return &s->s_map; 3161 } 3162 /* 3163 ** STRTORWSET -- convert string to rewriting set number 3164 ** 3165 ** Parameters: 3166 ** p -- the pointer to the string to decode. 3167 ** endp -- if set, store the trailing delimiter here. 3168 ** stabmode -- ST_ENTER to create this entry, ST_FIND if 3169 ** it must already exist. 3170 ** 3171 ** Returns: 3172 ** The appropriate ruleset number. 3173 ** -1 if it is not valid (error already printed) 3174 */ 3175 3176 int 3177 strtorwset(p, endp, stabmode) 3178 char *p; 3179 char **endp; 3180 int stabmode; 3181 { 3182 int ruleset; 3183 static int nextruleset = MAXRWSETS; 3184 3185 while (isascii(*p) && isspace(*p)) 3186 p++; 3187 if (!isascii(*p)) 3188 { 3189 syserr("invalid ruleset name: \"%.20s\"", p); 3190 return -1; 3191 } 3192 if (isdigit(*p)) 3193 { 3194 ruleset = strtol(p, endp, 10); 3195 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3196 { 3197 syserr("bad ruleset %d (%d max)", 3198 ruleset, MAXRWSETS / 2); 3199 ruleset = -1; 3200 } 3201 } 3202 else 3203 { 3204 STAB *s; 3205 char delim; 3206 char *q = NULL; 3207 3208 q = p; 3209 while (*p != '\0' && isascii(*p) && 3210 (isalnum(*p) || *p == '_')) 3211 p++; 3212 if (q == p || !(isascii(*q) && isalpha(*q))) 3213 { 3214 /* no valid characters */ 3215 syserr("invalid ruleset name: \"%.20s\"", q); 3216 return -1; 3217 } 3218 while (isascii(*p) && isspace(*p)) 3219 *p++ = '\0'; 3220 delim = *p; 3221 if (delim != '\0') 3222 *p = '\0'; 3223 s = stab(q, ST_RULESET, stabmode); 3224 if (delim != '\0') 3225 *p = delim; 3226 3227 if (s == NULL) 3228 return -1; 3229 3230 if (stabmode == ST_ENTER && delim == '=') 3231 { 3232 while (isascii(*++p) && isspace(*p)) 3233 continue; 3234 if (!(isascii(*p) && isdigit(*p))) 3235 { 3236 syserr("bad ruleset definition \"%s\" (number required after `=')", q); 3237 ruleset = -1; 3238 } 3239 else 3240 { 3241 ruleset = strtol(p, endp, 10); 3242 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3243 { 3244 syserr("bad ruleset number %d in \"%s\" (%d max)", 3245 ruleset, q, MAXRWSETS / 2); 3246 ruleset = -1; 3247 } 3248 } 3249 } 3250 else 3251 { 3252 if (endp != NULL) 3253 *endp = p; 3254 if (s->s_ruleset >= 0) 3255 ruleset = s->s_ruleset; 3256 else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 3257 { 3258 syserr("%s: too many named rulesets (%d max)", 3259 q, MAXRWSETS / 2); 3260 ruleset = -1; 3261 } 3262 } 3263 if (s->s_ruleset >= 0 && 3264 ruleset >= 0 && 3265 ruleset != s->s_ruleset) 3266 { 3267 syserr("%s: ruleset changed value (old %d, new %d)", 3268 q, s->s_ruleset, ruleset); 3269 ruleset = s->s_ruleset; 3270 } 3271 else if (ruleset >= 0) 3272 { 3273 s->s_ruleset = ruleset; 3274 } 3275 if (stabmode == ST_ENTER && ruleset >= 0) 3276 { 3277 char *h = NULL; 3278 3279 if (RuleSetNames[ruleset] != NULL) 3280 sm_free(RuleSetNames[ruleset]); 3281 if (delim != '\0' && (h = strchr(q, delim)) != NULL) 3282 *h = '\0'; 3283 RuleSetNames[ruleset] = newstr(q); 3284 if (delim == '/' && h != NULL) 3285 *h = delim; /* put back delim */ 3286 } 3287 } 3288 return ruleset; 3289 } 3290 /* 3291 ** SETTIMEOUT -- set an individual timeout 3292 ** 3293 ** Parameters: 3294 ** name -- the name of the timeout. 3295 ** val -- the value of the timeout. 3296 ** sticky -- if set, don't let other setoptions override 3297 ** this value. 3298 ** 3299 ** Returns: 3300 ** none. 3301 */ 3302 3303 /* set if Timeout sub-option is stuck */ 3304 static BITMAP256 StickyTimeoutOpt; 3305 3306 static struct timeoutinfo 3307 { 3308 char *to_name; /* long name of timeout */ 3309 u_char to_code; /* code for option */ 3310 } TimeOutTab[] = 3311 { 3312 #define TO_INITIAL 0x01 3313 { "initial", TO_INITIAL }, 3314 #define TO_MAIL 0x02 3315 { "mail", TO_MAIL }, 3316 #define TO_RCPT 0x03 3317 { "rcpt", TO_RCPT }, 3318 #define TO_DATAINIT 0x04 3319 { "datainit", TO_DATAINIT }, 3320 #define TO_DATABLOCK 0x05 3321 { "datablock", TO_DATABLOCK }, 3322 #define TO_DATAFINAL 0x06 3323 { "datafinal", TO_DATAFINAL }, 3324 #define TO_COMMAND 0x07 3325 { "command", TO_COMMAND }, 3326 #define TO_RSET 0x08 3327 { "rset", TO_RSET }, 3328 #define TO_HELO 0x09 3329 { "helo", TO_HELO }, 3330 #define TO_QUIT 0x0A 3331 { "quit", TO_QUIT }, 3332 #define TO_MISC 0x0B 3333 { "misc", TO_MISC }, 3334 #define TO_IDENT 0x0C 3335 { "ident", TO_IDENT }, 3336 #define TO_FILEOPEN 0x0D 3337 { "fileopen", TO_FILEOPEN }, 3338 #define TO_CONNECT 0x0E 3339 { "connect", TO_CONNECT }, 3340 #define TO_ICONNECT 0x0F 3341 { "iconnect", TO_ICONNECT }, 3342 #define TO_QUEUEWARN 0x10 3343 { "queuewarn", TO_QUEUEWARN }, 3344 { "queuewarn.*", TO_QUEUEWARN }, 3345 #define TO_QUEUEWARN_NORMAL 0x11 3346 { "queuewarn.normal", TO_QUEUEWARN_NORMAL }, 3347 #define TO_QUEUEWARN_URGENT 0x12 3348 { "queuewarn.urgent", TO_QUEUEWARN_URGENT }, 3349 #define TO_QUEUEWARN_NON_URGENT 0x13 3350 { "queuewarn.non-urgent", TO_QUEUEWARN_NON_URGENT }, 3351 #define TO_QUEUERETURN 0x14 3352 { "queuereturn", TO_QUEUERETURN }, 3353 { "queuereturn.*", TO_QUEUERETURN }, 3354 #define TO_QUEUERETURN_NORMAL 0x15 3355 { "queuereturn.normal", TO_QUEUERETURN_NORMAL }, 3356 #define TO_QUEUERETURN_URGENT 0x16 3357 { "queuereturn.urgent", TO_QUEUERETURN_URGENT }, 3358 #define TO_QUEUERETURN_NON_URGENT 0x17 3359 { "queuereturn.non-urgent", TO_QUEUERETURN_NON_URGENT }, 3360 #define TO_HOSTSTATUS 0x18 3361 { "hoststatus", TO_HOSTSTATUS }, 3362 #define TO_RESOLVER_RETRANS 0x19 3363 { "resolver.retrans", TO_RESOLVER_RETRANS }, 3364 #define TO_RESOLVER_RETRANS_NORMAL 0x1A 3365 { "resolver.retrans.normal", TO_RESOLVER_RETRANS_NORMAL }, 3366 #define TO_RESOLVER_RETRANS_FIRST 0x1B 3367 { "resolver.retrans.first", TO_RESOLVER_RETRANS_FIRST }, 3368 #define TO_RESOLVER_RETRY 0x1C 3369 { "resolver.retry", TO_RESOLVER_RETRY }, 3370 #define TO_RESOLVER_RETRY_NORMAL 0x1D 3371 { "resolver.retry.normal", TO_RESOLVER_RETRY_NORMAL }, 3372 #define TO_RESOLVER_RETRY_FIRST 0x1E 3373 { "resolver.retry.first", TO_RESOLVER_RETRY_FIRST }, 3374 #define TO_CONTROL 0x1F 3375 { "control", TO_CONTROL }, 3376 { NULL, 0 }, 3377 }; 3378 3379 3380 static void 3381 settimeout(name, val, sticky) 3382 char *name; 3383 char *val; 3384 bool sticky; 3385 { 3386 register struct timeoutinfo *to; 3387 int i; 3388 int addopts; 3389 time_t toval; 3390 3391 if (tTd(37, 2)) 3392 dprintf("settimeout(%s = %s)", name, val); 3393 3394 for (to = TimeOutTab; to->to_name != NULL; to++) 3395 { 3396 if (strcasecmp(to->to_name, name) == 0) 3397 break; 3398 } 3399 3400 if (to->to_name == NULL) 3401 { 3402 errno = 0; /* avoid bogus error text */ 3403 syserr("settimeout: invalid timeout %s", name); 3404 return; 3405 } 3406 3407 /* 3408 ** See if this option is preset for us. 3409 */ 3410 3411 if (!sticky && bitnset(to->to_code, StickyTimeoutOpt)) 3412 { 3413 if (tTd(37, 2)) 3414 dprintf(" (ignored)\n"); 3415 return; 3416 } 3417 3418 if (tTd(37, 2)) 3419 dprintf("\n"); 3420 3421 toval = convtime(val, 'm'); 3422 addopts = 0; 3423 3424 switch (to->to_code) 3425 { 3426 case TO_INITIAL: 3427 TimeOuts.to_initial = toval; 3428 break; 3429 3430 case TO_MAIL: 3431 TimeOuts.to_mail = toval; 3432 break; 3433 3434 case TO_RCPT: 3435 TimeOuts.to_rcpt = toval; 3436 break; 3437 3438 case TO_DATAINIT: 3439 TimeOuts.to_datainit = toval; 3440 break; 3441 3442 case TO_DATABLOCK: 3443 TimeOuts.to_datablock = toval; 3444 break; 3445 3446 case TO_DATAFINAL: 3447 TimeOuts.to_datafinal = toval; 3448 break; 3449 3450 case TO_COMMAND: 3451 TimeOuts.to_nextcommand = toval; 3452 break; 3453 3454 case TO_RSET: 3455 TimeOuts.to_rset = toval; 3456 break; 3457 3458 case TO_HELO: 3459 TimeOuts.to_helo = toval; 3460 break; 3461 3462 case TO_QUIT: 3463 TimeOuts.to_quit = toval; 3464 break; 3465 3466 case TO_MISC: 3467 TimeOuts.to_miscshort = toval; 3468 break; 3469 3470 case TO_IDENT: 3471 TimeOuts.to_ident = toval; 3472 break; 3473 3474 case TO_FILEOPEN: 3475 TimeOuts.to_fileopen = toval; 3476 break; 3477 3478 case TO_CONNECT: 3479 TimeOuts.to_connect = toval; 3480 break; 3481 3482 case TO_ICONNECT: 3483 TimeOuts.to_iconnect = toval; 3484 break; 3485 3486 case TO_QUEUEWARN: 3487 toval = convtime(val, 'h'); 3488 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3489 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3490 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3491 addopts = 2; 3492 break; 3493 3494 case TO_QUEUEWARN_NORMAL: 3495 toval = convtime(val, 'h'); 3496 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3497 break; 3498 3499 case TO_QUEUEWARN_URGENT: 3500 toval = convtime(val, 'h'); 3501 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3502 break; 3503 3504 case TO_QUEUEWARN_NON_URGENT: 3505 toval = convtime(val, 'h'); 3506 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3507 break; 3508 3509 case TO_QUEUERETURN: 3510 toval = convtime(val, 'd'); 3511 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3512 TimeOuts.to_q_return[TOC_URGENT] = toval; 3513 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3514 addopts = 2; 3515 break; 3516 3517 case TO_QUEUERETURN_NORMAL: 3518 toval = convtime(val, 'd'); 3519 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3520 break; 3521 3522 case TO_QUEUERETURN_URGENT: 3523 toval = convtime(val, 'd'); 3524 TimeOuts.to_q_return[TOC_URGENT] = toval; 3525 break; 3526 3527 case TO_QUEUERETURN_NON_URGENT: 3528 toval = convtime(val, 'd'); 3529 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3530 break; 3531 3532 3533 case TO_HOSTSTATUS: 3534 MciInfoTimeout = toval; 3535 break; 3536 3537 case TO_RESOLVER_RETRANS: 3538 toval = convtime(val, 's'); 3539 TimeOuts.res_retrans[RES_TO_DEFAULT] = toval; 3540 TimeOuts.res_retrans[RES_TO_FIRST] = toval; 3541 TimeOuts.res_retrans[RES_TO_NORMAL] = toval; 3542 addopts = 2; 3543 break; 3544 3545 case TO_RESOLVER_RETRY: 3546 i = atoi(val); 3547 TimeOuts.res_retry[RES_TO_DEFAULT] = i; 3548 TimeOuts.res_retry[RES_TO_FIRST] = i; 3549 TimeOuts.res_retry[RES_TO_NORMAL] = i; 3550 addopts = 2; 3551 break; 3552 3553 case TO_RESOLVER_RETRANS_NORMAL: 3554 TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's'); 3555 break; 3556 3557 case TO_RESOLVER_RETRY_NORMAL: 3558 TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val); 3559 break; 3560 3561 case TO_RESOLVER_RETRANS_FIRST: 3562 TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's'); 3563 break; 3564 3565 case TO_RESOLVER_RETRY_FIRST: 3566 TimeOuts.res_retry[RES_TO_FIRST] = atoi(val); 3567 break; 3568 3569 case TO_CONTROL: 3570 TimeOuts.to_control = toval; 3571 break; 3572 3573 default: 3574 syserr("settimeout: invalid timeout %s", name); 3575 break; 3576 } 3577 3578 if (sticky) 3579 { 3580 for (i = 0; i <= addopts; i++) 3581 setbitn(to->to_code + i, StickyTimeoutOpt); 3582 } 3583 } 3584 /* 3585 ** INITTIMEOUTS -- parse and set timeout values 3586 ** 3587 ** Parameters: 3588 ** val -- a pointer to the values. If NULL, do initial 3589 ** settings. 3590 ** sticky -- if set, don't let other setoptions override 3591 ** this suboption value. 3592 ** 3593 ** Returns: 3594 ** none. 3595 ** 3596 ** Side Effects: 3597 ** Initializes the TimeOuts structure 3598 */ 3599 3600 void 3601 inittimeouts(val, sticky) 3602 register char *val; 3603 bool sticky; 3604 { 3605 register char *p; 3606 3607 if (tTd(37, 2)) 3608 dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); 3609 if (val == NULL) 3610 { 3611 TimeOuts.to_connect = (time_t) 0 SECONDS; 3612 TimeOuts.to_initial = (time_t) 5 MINUTES; 3613 TimeOuts.to_helo = (time_t) 5 MINUTES; 3614 TimeOuts.to_mail = (time_t) 10 MINUTES; 3615 TimeOuts.to_rcpt = (time_t) 1 HOUR; 3616 TimeOuts.to_datainit = (time_t) 5 MINUTES; 3617 TimeOuts.to_datablock = (time_t) 1 HOUR; 3618 TimeOuts.to_datafinal = (time_t) 1 HOUR; 3619 TimeOuts.to_rset = (time_t) 5 MINUTES; 3620 TimeOuts.to_quit = (time_t) 2 MINUTES; 3621 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 3622 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 3623 #if IDENTPROTO 3624 TimeOuts.to_ident = (time_t) 5 SECONDS; 3625 #else /* IDENTPROTO */ 3626 TimeOuts.to_ident = (time_t) 0 SECONDS; 3627 #endif /* IDENTPROTO */ 3628 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 3629 TimeOuts.to_control = (time_t) 2 MINUTES; 3630 if (tTd(37, 5)) 3631 { 3632 dprintf("Timeouts:\n"); 3633 dprintf(" connect = %ld\n", (long)TimeOuts.to_connect); 3634 dprintf(" initial = %ld\n", (long)TimeOuts.to_initial); 3635 dprintf(" helo = %ld\n", (long)TimeOuts.to_helo); 3636 dprintf(" mail = %ld\n", (long)TimeOuts.to_mail); 3637 dprintf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); 3638 dprintf(" datainit = %ld\n", (long)TimeOuts.to_datainit); 3639 dprintf(" datablock = %ld\n", (long)TimeOuts.to_datablock); 3640 dprintf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); 3641 dprintf(" rset = %ld\n", (long)TimeOuts.to_rset); 3642 dprintf(" quit = %ld\n", (long)TimeOuts.to_quit); 3643 dprintf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); 3644 dprintf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); 3645 dprintf(" ident = %ld\n", (long)TimeOuts.to_ident); 3646 dprintf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); 3647 dprintf(" control = %ld\n", (long)TimeOuts.to_control); 3648 } 3649 return; 3650 } 3651 3652 for (;; val = p) 3653 { 3654 while (isascii(*val) && isspace(*val)) 3655 val++; 3656 if (*val == '\0') 3657 break; 3658 for (p = val; *p != '\0' && *p != ','; p++) 3659 continue; 3660 if (*p != '\0') 3661 *p++ = '\0'; 3662 3663 if (isascii(*val) && isdigit(*val)) 3664 { 3665 /* old syntax -- set everything */ 3666 TimeOuts.to_mail = convtime(val, 'm'); 3667 TimeOuts.to_rcpt = TimeOuts.to_mail; 3668 TimeOuts.to_datainit = TimeOuts.to_mail; 3669 TimeOuts.to_datablock = TimeOuts.to_mail; 3670 TimeOuts.to_datafinal = TimeOuts.to_mail; 3671 TimeOuts.to_nextcommand = TimeOuts.to_mail; 3672 if (sticky) 3673 { 3674 setbitn(TO_MAIL, StickyTimeoutOpt); 3675 setbitn(TO_RCPT, StickyTimeoutOpt); 3676 setbitn(TO_DATAINIT, StickyTimeoutOpt); 3677 setbitn(TO_DATABLOCK, StickyTimeoutOpt); 3678 setbitn(TO_DATAFINAL, StickyTimeoutOpt); 3679 setbitn(TO_COMMAND, StickyTimeoutOpt); 3680 } 3681 continue; 3682 } 3683 else 3684 { 3685 register char *q = strchr(val, ':'); 3686 3687 if (q == NULL && (q = strchr(val, '=')) == NULL) 3688 { 3689 /* syntax error */ 3690 continue; 3691 } 3692 *q++ = '\0'; 3693 settimeout(val, q, sticky); 3694 } 3695 } 3696 } 3697