]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/sendmail/src/readcf.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / sendmail / src / readcf.c
1 /*
2  * Copyright (c) 1998-2006, 2008 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 #include <sendmail.h>
15 #include <sm/sendmail.h>
16
17 SM_RCSID("@(#)$Id: readcf.c,v 8.666 2008/02/14 17:25:14 ca Exp $")
18
19 #if NETINET || NETINET6
20 # include <arpa/inet.h>
21 #endif /* NETINET || NETINET6 */
22
23 #define SECONDS
24 #define MINUTES * 60
25 #define HOUR    * 3600
26 #define HOURS   HOUR
27
28 static void     fileclass __P((int, char *, char *, bool, bool, bool));
29 static char     **makeargv __P((char *));
30 static void     settimeout __P((char *, char *, bool));
31 static void     toomany __P((int, int));
32 static char     *extrquotstr __P((char *, char **, char *, bool *));
33 static void     parse_class_words __P((int, char *));
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 **              O option value  Set option (long name) to value.
61 **              Pname=value     Set precedence name to value.
62 **              Qn arg=val...   Define queue groups.  n is the internal name.
63 **                              Args specify queue parameters.
64 **              Vversioncode[/vendorcode]
65 **                              Version level/vendor name of
66 **                              configuration syntax.
67 **              Kmapname mapclass arguments....
68 **                              Define keyed lookup of a given class.
69 **                              Arguments are class dependent.
70 **              Eenvar=value    Set the environment value to the given value.
71 **
72 **      Parameters:
73 **              cfname -- configuration file name.
74 **              safe -- true if this is the system config file;
75 **                      false otherwise.
76 **              e -- the main envelope.
77 **
78 **      Returns:
79 **              none.
80 **
81 **      Side Effects:
82 **              Builds several internal tables.
83 */
84
85 void
86 readcf(cfname, safe, e)
87         char *cfname;
88         bool safe;
89         register ENVELOPE *e;
90 {
91         SM_FILE_T *cf;
92         int ruleset = -1;
93         char *q;
94         struct rewrite *rwp = NULL;
95         char *bp;
96         auto char *ep;
97         int nfuzzy;
98         char *file;
99         bool optional;
100         bool ok;
101         bool ismap;
102         int mid;
103         register char *p;
104         long sff = SFF_OPENASROOT;
105         struct stat statb;
106         char buf[MAXLINE];
107         int bufsize;
108         char exbuf[MAXLINE];
109         char pvpbuf[MAXLINE + MAXATOM];
110         static char *null_list[1] = { NULL };
111         extern unsigned char TokTypeNoC[];
112
113         FileName = cfname;
114         LineNumber = 0;
115
116         if (DontLockReadFiles)
117                 sff |= SFF_NOLOCK;
118         cf = safefopen(cfname, O_RDONLY, 0444, sff);
119         if (cf == NULL)
120         {
121                 syserr("cannot open");
122                 finis(false, true, EX_OSFILE);
123         }
124
125         if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
126         {
127                 syserr("cannot fstat");
128                 finis(false, true, EX_OSFILE);
129         }
130
131         if (!S_ISREG(statb.st_mode))
132         {
133                 syserr("not a plain file");
134                 finis(false, true, EX_OSFILE);
135         }
136
137         if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
138         {
139                 if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
140                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
141                                              "%s: WARNING: dangerous write permissions\n",
142                                              FileName);
143                 if (LogLevel > 0)
144                         sm_syslog(LOG_CRIT, NOQID,
145                                   "%s: WARNING: dangerous write permissions",
146                                   FileName);
147         }
148
149 #if XLA
150         xla_zero();
151 #endif /* XLA */
152
153         while (bufsize = sizeof(buf),
154                (bp = fgetfolded(buf, &bufsize, cf)) != NULL)
155         {
156                 char *nbp;
157
158                 if (bp[0] == '#')
159                 {
160                         if (bp != buf)
161                                 sm_free(bp); /* XXX */
162                         continue;
163                 }
164
165                 /* do macro expansion mappings */
166                 nbp = translate_dollars(bp, bp, &bufsize);
167                 if (nbp != bp && bp != buf)
168                         sm_free(bp);
169                 bp = nbp;
170
171                 /* interpret this line */
172                 errno = 0;
173                 switch (bp[0])
174                 {
175                   case '\0':
176                   case '#':             /* comment */
177                         break;
178
179                   case 'R':             /* rewriting rule */
180                         if (ruleset < 0)
181                         {
182                                 syserr("missing valid ruleset for \"%s\"", bp);
183                                 break;
184                         }
185                         for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
186                                 continue;
187
188                         if (*p == '\0')
189                         {
190                                 syserr("invalid rewrite line \"%s\" (tab expected)", bp);
191                                 break;
192                         }
193
194                         /* allocate space for the rule header */
195                         if (rwp == NULL)
196                         {
197                                 RewriteRules[ruleset] = rwp =
198                                         (struct rewrite *) xalloc(sizeof(*rwp));
199                         }
200                         else
201                         {
202                                 rwp->r_next = (struct rewrite *) xalloc(sizeof(*rwp));
203                                 rwp = rwp->r_next;
204                         }
205                         rwp->r_next = NULL;
206
207                         /* expand and save the LHS */
208                         *p = '\0';
209                         expand(&bp[1], exbuf, sizeof(exbuf), e);
210                         rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
211                                              sizeof(pvpbuf), NULL,
212                                              ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
213                                              true);
214                         nfuzzy = 0;
215                         if (rwp->r_lhs != NULL)
216                         {
217                                 register char **ap;
218
219                                 rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
220
221                                 /* count the number of fuzzy matches in LHS */
222                                 for (ap = rwp->r_lhs; *ap != NULL; ap++)
223                                 {
224                                         char *botch;
225
226                                         botch = NULL;
227                                         switch (ap[0][0] & 0377)
228                                         {
229                                           case MATCHZANY:
230                                           case MATCHANY:
231                                           case MATCHONE:
232                                           case MATCHCLASS:
233                                           case MATCHNCLASS:
234                                                 nfuzzy++;
235                                                 break;
236
237                                           case MATCHREPL:
238                                                 botch = "$1-$9";
239                                                 break;
240
241                                           case CANONUSER:
242                                                 botch = "$:";
243                                                 break;
244
245                                           case CALLSUBR:
246                                                 botch = "$>";
247                                                 break;
248
249                                           case CONDIF:
250                                                 botch = "$?";
251                                                 break;
252
253                                           case CONDFI:
254                                                 botch = "$.";
255                                                 break;
256
257                                           case HOSTBEGIN:
258                                                 botch = "$[";
259                                                 break;
260
261                                           case HOSTEND:
262                                                 botch = "$]";
263                                                 break;
264
265                                           case LOOKUPBEGIN:
266                                                 botch = "$(";
267                                                 break;
268
269                                           case LOOKUPEND:
270                                                 botch = "$)";
271                                                 break;
272                                         }
273                                         if (botch != NULL)
274                                                 syserr("Inappropriate use of %s on LHS",
275                                                         botch);
276                                 }
277                                 rwp->r_line = LineNumber;
278                         }
279                         else
280                         {
281                                 syserr("R line: null LHS");
282                                 rwp->r_lhs = null_list;
283                         }
284                         if (nfuzzy > MAXMATCH)
285                         {
286                                 syserr("R line: too many wildcards");
287                                 rwp->r_lhs = null_list;
288                         }
289
290                         /* expand and save the RHS */
291                         while (*++p == '\t')
292                                 continue;
293                         q = p;
294                         while (*p != '\0' && *p != '\t')
295                                 p++;
296                         *p = '\0';
297                         expand(q, exbuf, sizeof(exbuf), e);
298                         rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
299                                              sizeof(pvpbuf), NULL,
300                                              ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
301                                              true);
302                         if (rwp->r_rhs != NULL)
303                         {
304                                 register char **ap;
305                                 int args, endtoken;
306 #if _FFR_EXTRA_MAP_CHECK
307                                 int nexttoken;
308 #endif /* _FFR_EXTRA_MAP_CHECK */
309                                 bool inmap;
310
311                                 rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
312
313                                 /* check no out-of-bounds replacements */
314                                 nfuzzy += '0';
315                                 inmap = false;
316                                 args = 0;
317                                 endtoken = 0;
318                                 for (ap = rwp->r_rhs; *ap != NULL; ap++)
319                                 {
320                                         char *botch;
321
322                                         botch = NULL;
323                                         switch (ap[0][0] & 0377)
324                                         {
325                                           case MATCHREPL:
326                                                 if (ap[0][1] <= '0' ||
327                                                     ap[0][1] > nfuzzy)
328                                                 {
329                                                         syserr("replacement $%c out of bounds",
330                                                                 ap[0][1]);
331                                                 }
332                                                 break;
333
334                                           case MATCHZANY:
335                                                 botch = "$*";
336                                                 break;
337
338                                           case MATCHANY:
339                                                 botch = "$+";
340                                                 break;
341
342                                           case MATCHONE:
343                                                 botch = "$-";
344                                                 break;
345
346                                           case MATCHCLASS:
347                                                 botch = "$=";
348                                                 break;
349
350                                           case MATCHNCLASS:
351                                                 botch = "$~";
352                                                 break;
353
354                                           case CANONHOST:
355                                                 if (!inmap)
356                                                         break;
357                                                 if (++args >= MAX_MAP_ARGS)
358                                                         syserr("too many arguments for map lookup");
359                                                 break;
360
361                                           case HOSTBEGIN:
362                                                 endtoken = HOSTEND;
363                                                 /* FALLTHROUGH */
364                                           case LOOKUPBEGIN:
365                                                 /* see above... */
366                                                 if ((ap[0][0] & 0377) == LOOKUPBEGIN)
367                                                         endtoken = LOOKUPEND;
368                                                 if (inmap)
369                                                         syserr("cannot nest map lookups");
370                                                 inmap = true;
371                                                 args = 0;
372 #if _FFR_EXTRA_MAP_CHECK
373                                                 if (ap[1] == NULL)
374                                                 {
375                                                         syserr("syntax error in map lookup");
376                                                         break;
377                                                 }
378                                                 nexttoken = ap[1][0] & 0377;
379                                                 if (nexttoken == CANONHOST ||
380                                                     nexttoken == CANONUSER ||
381                                                     nexttoken == endtoken))
382                                                 {
383                                                         syserr("missing map name for lookup");
384                                                         break;
385                                                 }
386                                                 if (ap[2] == NULL)
387                                                 {
388                                                         syserr("syntax error in map lookup");
389                                                         break;
390                                                 }
391                                                 if (ap[0][0] == HOSTBEGIN)
392                                                         break;
393                                                 nexttoken = ap[2][0] & 0377;
394                                                 if (nexttoken == CANONHOST ||
395                                                     nexttoken == CANONUSER ||
396                                                     nexttoken == endtoken)
397                                                 {
398                                                         syserr("missing key name for lookup");
399                                                         break;
400                                                 }
401 #endif /* _FFR_EXTRA_MAP_CHECK */
402                                                 break;
403
404                                           case HOSTEND:
405                                           case LOOKUPEND:
406                                                 if ((ap[0][0] & 0377) != endtoken)
407                                                         break;
408                                                 inmap = false;
409                                                 endtoken = 0;
410                                                 break;
411
412
413 #if 0
414 /*
415 **  This doesn't work yet as there are maps defined *after* the cf
416 **  is read such as host, user, and alias.  So for now, it's removed.
417 **  When it comes back, the RELEASE_NOTES entry will be:
418 **      Emit warnings for unknown maps when reading the .cf file.  Based on
419 **              patch from Robert Harker of Harker Systems.
420 */
421
422                                           case LOOKUPBEGIN:
423                                                 /*
424                                                 **  Got a database lookup,
425                                                 **  check if map is defined.
426                                                 */
427
428                                                 ep = ap[1];
429                                                 if ((ep[0] & 0377) != MACRODEXPAND &&
430                                                     stab(ep, ST_MAP, ST_FIND) == NULL)
431                                                 {
432                                                         (void) sm_io_fprintf(smioout,
433                                                                              SM_TIME_DEFAULT,
434                                                                              "Warning: %s: line %d: map %s not found\n",
435                                                                              FileName,
436                                                                              LineNumber,
437                                                                              ep);
438                                                 }
439                                                 break;
440 #endif /* 0 */
441                                         }
442                                         if (botch != NULL)
443                                                 syserr("Inappropriate use of %s on RHS",
444                                                         botch);
445                                 }
446                                 if (inmap)
447                                         syserr("missing map closing token");
448                         }
449                         else
450                         {
451                                 syserr("R line: null RHS");
452                                 rwp->r_rhs = null_list;
453                         }
454                         break;
455
456                   case 'S':             /* select rewriting set */
457                         expand(&bp[1], exbuf, sizeof(exbuf), e);
458                         ruleset = strtorwset(exbuf, NULL, ST_ENTER);
459                         if (ruleset < 0)
460                                 break;
461
462                         rwp = RewriteRules[ruleset];
463                         if (rwp != NULL)
464                         {
465                                 if (OpMode == MD_TEST)
466                                         (void) sm_io_fprintf(smioout,
467                                                              SM_TIME_DEFAULT,
468                                                              "WARNING: Ruleset %s has multiple definitions\n",
469                                                             &bp[1]);
470                                 if (tTd(37, 1))
471                                         sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
472                                                    &bp[1]);
473                                 while (rwp->r_next != NULL)
474                                         rwp = rwp->r_next;
475                         }
476                         break;
477
478                   case 'D':             /* macro definition */
479                         mid = macid_parse(&bp[1], &ep);
480                         if (mid == 0)
481                                 break;
482                         p = munchstring(ep, NULL, '\0');
483                         macdefine(&e->e_macro, A_TEMP, mid, p);
484                         break;
485
486                   case 'H':             /* required header line */
487                         (void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
488                         break;
489
490                   case 'C':             /* word class */
491                   case 'T':             /* trusted user (set class `t') */
492                         if (bp[0] == 'C')
493                         {
494                                 mid = macid_parse(&bp[1], &ep);
495                                 if (mid == 0)
496                                         break;
497                                 expand(ep, exbuf, sizeof(exbuf), e);
498                                 p = exbuf;
499                         }
500                         else
501                         {
502                                 mid = 't';
503                                 p = &bp[1];
504                         }
505                         while (*p != '\0')
506                         {
507                                 register char *wd;
508                                 char delim;
509
510                                 while (*p != '\0' && isascii(*p) && isspace(*p))
511                                         p++;
512                                 wd = p;
513                                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
514                                         p++;
515                                 delim = *p;
516                                 *p = '\0';
517                                 if (wd[0] != '\0')
518                                         setclass(mid, wd);
519                                 *p = delim;
520                         }
521                         break;
522
523                   case 'F':             /* word class from file */
524                         mid = macid_parse(&bp[1], &ep);
525                         if (mid == 0)
526                                 break;
527                         for (p = ep; isascii(*p) && isspace(*p); )
528                                 p++;
529                         if (p[0] == '-' && p[1] == 'o')
530                         {
531                                 optional = true;
532                                 while (*p != '\0' &&
533                                        !(isascii(*p) && isspace(*p)))
534                                         p++;
535                                 while (isascii(*p) && isspace(*p))
536                                         p++;
537                                 file = p;
538                         }
539                         else
540                                 optional = false;
541
542                         /* check if [key]@map:spec */
543                         ismap = false;
544                         if (!SM_IS_DIR_DELIM(*p) &&
545                             *p != '|' &&
546                             (q = strchr(p, '@')) != NULL)
547                         {
548                                 q++;
549
550                                 /* look for @LDAP or @map: in string */
551                                 if (strcmp(q, "LDAP") == 0 ||
552                                     (*q != ':' &&
553                                      strchr(q, ':') != NULL))
554                                         ismap = true;
555                         }
556
557                         if (ismap)
558                         {
559                                 /* use entire spec */
560                                 file = p;
561                         }
562                         else
563                         {
564                                 file = extrquotstr(p, &q, " ", &ok);
565                                 if (!ok)
566                                 {
567                                         syserr("illegal filename '%s'", p);
568                                         break;
569                                 }
570                         }
571
572                         if (*file == '|' || ismap)
573                                 p = "%s";
574                         else
575                         {
576                                 p = q;
577                                 if (*p == '\0')
578                                         p = "%s";
579                                 else
580                                 {
581                                         *p = '\0';
582                                         while (isascii(*++p) && isspace(*p))
583                                                 continue;
584                                 }
585                         }
586                         fileclass(mid, file, p, ismap, safe, optional);
587                         break;
588
589 #if XLA
590                   case 'L':             /* extended load average description */
591                         xla_init(&bp[1]);
592                         break;
593 #endif /* XLA */
594
595 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
596                   case 'L':             /* lookup macro */
597                   case 'G':             /* lookup class */
598                         /* reserved for Sun -- NIS+ database lookup */
599                         if (VendorCode != VENDOR_SUN)
600                                 goto badline;
601                         sun_lg_config_line(bp, e);
602                         break;
603 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
604
605                   case 'M':             /* define mailer */
606                         makemailer(&bp[1]);
607                         break;
608
609                   case 'O':             /* set option */
610                         setoption(bp[1], &bp[2], safe, false, e);
611                         break;
612
613                   case 'P':             /* set precedence */
614                         if (NumPriorities >= MAXPRIORITIES)
615                         {
616                                 toomany('P', MAXPRIORITIES);
617                                 break;
618                         }
619                         for (p = &bp[1]; *p != '\0' && *p != '='; p++)
620                                 continue;
621                         if (*p == '\0')
622                                 goto badline;
623                         *p = '\0';
624                         Priorities[NumPriorities].pri_name = newstr(&bp[1]);
625                         Priorities[NumPriorities].pri_val = atoi(++p);
626                         NumPriorities++;
627                         break;
628
629                   case 'Q':             /* define queue */
630                         makequeue(&bp[1], true);
631                         break;
632
633                   case 'V':             /* configuration syntax version */
634                         for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
635                                 continue;
636                         if (!isascii(*p) || !isdigit(*p))
637                         {
638                                 syserr("invalid argument to V line: \"%.20s\"",
639                                         &bp[1]);
640                                 break;
641                         }
642                         ConfigLevel = strtol(p, &ep, 10);
643
644                         /*
645                         **  Do heuristic tweaking for back compatibility.
646                         */
647
648                         if (ConfigLevel >= 5)
649                         {
650                                 /* level 5 configs have short name in $w */
651                                 p = macvalue('w', e);
652                                 if (p != NULL && (p = strchr(p, '.')) != NULL)
653                                 {
654                                         *p = '\0';
655                                         macdefine(&e->e_macro, A_TEMP, 'w',
656                                                   macvalue('w', e));
657                                 }
658                         }
659                         if (ConfigLevel >= 6)
660                         {
661                                 ColonOkInAddr = false;
662                         }
663
664                         /*
665                         **  Look for vendor code.
666                         */
667
668                         if (*ep++ == '/')
669                         {
670                                 /* extract vendor code */
671                                 for (p = ep; isascii(*p) && isalpha(*p); )
672                                         p++;
673                                 *p = '\0';
674
675                                 if (!setvendor(ep))
676                                         syserr("invalid V line vendor code: \"%s\"",
677                                                 ep);
678                         }
679                         break;
680
681                   case 'K':
682                         expand(&bp[1], exbuf, sizeof(exbuf), e);
683                         (void) makemapentry(exbuf);
684                         break;
685
686                   case 'E':
687                         p = strchr(bp, '=');
688                         if (p != NULL)
689                                 *p++ = '\0';
690                         sm_setuserenv(&bp[1], p);
691                         break;
692
693                   case 'X':             /* mail filter */
694 #if MILTER
695                         milter_setup(&bp[1]);
696 #else /* MILTER */
697                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
698                                              "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
699 #endif /* MILTER */
700                         break;
701
702                   default:
703                   badline:
704                         syserr("unknown configuration line \"%s\"", bp);
705                 }
706                 if (bp != buf)
707                         sm_free(bp); /* XXX */
708         }
709         if (sm_io_error(cf))
710         {
711                 syserr("I/O read error");
712                 finis(false, true, EX_OSFILE);
713         }
714         (void) sm_io_close(cf, SM_TIME_DEFAULT);
715         FileName = NULL;
716
717         /* initialize host maps from local service tables */
718         inithostmaps();
719
720         /* initialize daemon (if not defined yet) */
721         initdaemon();
722
723         /* determine if we need to do special name-server frotz */
724         {
725                 int nmaps;
726                 char *maptype[MAXMAPSTACK];
727                 short mapreturn[MAXMAPACTIONS];
728
729                 nmaps = switch_map_find("hosts", maptype, mapreturn);
730                 UseNameServer = false;
731                 if (nmaps > 0 && nmaps <= MAXMAPSTACK)
732                 {
733                         register int mapno;
734
735                         for (mapno = 0; mapno < nmaps && !UseNameServer;
736                              mapno++)
737                         {
738                                 if (strcmp(maptype[mapno], "dns") == 0)
739                                         UseNameServer = true;
740                         }
741                 }
742         }
743 }
744
745 /*
746 **  TRANSLATE_DOLLARS -- convert $x into internal form
747 **
748 **      Actually does all appropriate pre-processing of a config line
749 **      to turn it into internal form.
750 **
751 **      Parameters:
752 **              ibp -- the buffer to translate.
753 **              obp -- where to put the translation; may be the same as obp
754 **              bsp -- a pointer to the size of obp; will be updated if
755 **                      the buffer needs to be replaced.
756 **
757 **      Returns:
758 **              The buffer pointer; may differ from obp if the expansion
759 **              is larger then *bsp, in which case this will point to
760 **              malloc()ed memory which must be free()d by the caller.
761 */
762
763 char *
764 translate_dollars(ibp, obp, bsp)
765         char *ibp;
766         char *obp;
767         int *bsp;
768 {
769         register char *p;
770         auto char *ep;
771         char *bp;
772
773         if (tTd(37, 53))
774         {
775                 sm_dprintf("translate_dollars(");
776                 xputs(sm_debug_file(), ibp);
777                 sm_dprintf(")\n");
778         }
779
780         bp = quote_internal_chars(ibp, obp, bsp);
781
782         for (p = bp; *p != '\0'; p++)
783         {
784                 if (*p == '#' && p > bp && ConfigLevel >= 3)
785                 {
786                         register char *e;
787
788                         switch (*--p & 0377)
789                         {
790                           case MACROEXPAND:
791                                 /* it's from $# -- let it go through */
792                                 p++;
793                                 break;
794
795                           case '\\':
796                                 /* it's backslash escaped */
797                                 (void) sm_strlcpy(p, p + 1, strlen(p));
798                                 break;
799
800                           default:
801                                 /* delete leading white space */
802                                 while (isascii(*p) && isspace(*p) &&
803                                        *p != '\n' && p > bp)
804                                 {
805                                         p--;
806                                 }
807                                 if ((e = strchr(++p, '\n')) != NULL)
808                                         (void) sm_strlcpy(p, e, strlen(p));
809                                 else
810                                         *p-- = '\0';
811                                 break;
812                         }
813                         continue;
814                 }
815
816                 if (*p != '$' || p[1] == '\0')
817                         continue;
818
819                 if (p[1] == '$')
820                 {
821                         /* actual dollar sign.... */
822                         (void) sm_strlcpy(p, p + 1, strlen(p));
823                         continue;
824                 }
825
826                 /* convert to macro expansion character */
827                 *p++ = MACROEXPAND;
828
829                 /* special handling for $=, $~, $&, and $? */
830                 if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
831                         p++;
832
833                 /* convert macro name to code */
834                 *p = macid_parse(p, &ep);
835                 if (ep != p + 1)
836                         (void) sm_strlcpy(p + 1, ep, strlen(p + 1));
837         }
838
839         /* strip trailing white space from the line */
840         while (--p > bp && isascii(*p) && isspace(*p))
841                 *p = '\0';
842
843         if (tTd(37, 53))
844         {
845                 sm_dprintf("  translate_dollars => ");
846                 xputs(sm_debug_file(), bp);
847                 sm_dprintf("\n");
848         }
849
850         return bp;
851 }
852 /*
853 **  TOOMANY -- signal too many of some option
854 **
855 **      Parameters:
856 **              id -- the id of the error line
857 **              maxcnt -- the maximum possible values
858 **
859 **      Returns:
860 **              none.
861 **
862 **      Side Effects:
863 **              gives a syserr.
864 */
865
866 static void
867 toomany(id, maxcnt)
868         int id;
869         int maxcnt;
870 {
871         syserr("too many %c lines, %d max", id, maxcnt);
872 }
873 /*
874 **  FILECLASS -- read members of a class from a file
875 **
876 **      Parameters:
877 **              class -- class to define.
878 **              filename -- name of file to read.
879 **              fmt -- scanf string to use for match.
880 **              ismap -- if set, this is a map lookup.
881 **              safe -- if set, this is a safe read.
882 **              optional -- if set, it is not an error for the file to
883 **                      not exist.
884 **
885 **      Returns:
886 **              none
887 **
888 **      Side Effects:
889 **              puts all lines in filename that match a scanf into
890 **                      the named class.
891 */
892
893 /*
894 **  Break up the match into words and add to class.
895 */
896
897 static void
898 parse_class_words(class, line)
899         int class;
900         char *line;
901 {
902         while (line != NULL && *line != '\0')
903         {
904                 register char *q;
905
906                 /* strip leading spaces */
907                 while (isascii(*line) && isspace(*line))
908                         line++;
909                 if (*line == '\0')
910                         break;
911
912                 /* find the end of the word */
913                 q = line;
914                 while (*line != '\0' && !(isascii(*line) && isspace(*line)))
915                         line++;
916                 if (*line != '\0')
917                         *line++ = '\0';
918
919                 /* enter the word in the symbol table */
920                 setclass(class, q);
921         }
922 }
923
924 static void
925 fileclass(class, filename, fmt, ismap, safe, optional)
926         int class;
927         char *filename;
928         char *fmt;
929         bool ismap;
930         bool safe;
931         bool optional;
932 {
933         SM_FILE_T *f;
934         long sff;
935         pid_t pid;
936         register char *p;
937         char buf[MAXLINE];
938
939         if (tTd(37, 2))
940                 sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
941
942         if (*filename == '\0')
943         {
944                 syserr("fileclass: missing file name");
945                 return;
946         }
947         else if (ismap)
948         {
949                 int status = 0;
950                 char *key;
951                 char *mn;
952                 char *cl, *spec;
953                 STAB *mapclass;
954                 MAP map;
955
956                 mn = newstr(macname(class));
957
958                 key = filename;
959
960                 /* skip past key */
961                 if ((p = strchr(filename, '@')) == NULL)
962                 {
963                         /* should not happen */
964                         syserr("fileclass: bogus map specification");
965                         sm_free(mn);
966                         return;
967                 }
968
969                 /* skip past '@' */
970                 *p++ = '\0';
971                 cl = p;
972
973 #if LDAPMAP
974                 if (strcmp(cl, "LDAP") == 0)
975                 {
976                         int n;
977                         char *lc;
978                         char jbuf[MAXHOSTNAMELEN];
979                         char lcbuf[MAXLINE];
980
981                         /* Get $j */
982                         expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
983                         if (jbuf[0] == '\0')
984                         {
985                                 (void) sm_strlcpy(jbuf, "localhost",
986                                                   sizeof(jbuf));
987                         }
988
989                         /* impose the default schema */
990                         lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
991                         if (lc == NULL)
992                                 lc = "";
993                         else
994                         {
995                                 expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
996                                 lc = lcbuf;
997                         }
998
999                         cl = "ldap";
1000                         n = sm_snprintf(buf, sizeof(buf),
1001                                         "-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue,sendmailMTAClassSearch:FILTER:sendmailMTAClass,sendmailMTAClassURL:URL:sendmailMTAClass",
1002                                         mn, lc, jbuf);
1003                         if (n >= sizeof(buf))
1004                         {
1005                                 syserr("fileclass: F{%s}: Default LDAP string too long",
1006                                        mn);
1007                                 sm_free(mn);
1008                                 return;
1009                         }
1010                         spec = buf;
1011                 }
1012                 else
1013 #endif /* LDAPMAP */
1014                 {
1015                         if ((spec = strchr(cl, ':')) == NULL)
1016                         {
1017                                 syserr("fileclass: F{%s}: missing map class",
1018                                        mn);
1019                                 sm_free(mn);
1020                                 return;
1021                         }
1022                         *spec++ ='\0';
1023                 }
1024
1025                 /* set up map structure */
1026                 mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
1027                 if (mapclass == NULL)
1028                 {
1029                         syserr("fileclass: F{%s}: class %s not available",
1030                                mn, cl);
1031                         sm_free(mn);
1032                         return;
1033                 }
1034                 memset(&map, '\0', sizeof(map));
1035                 map.map_class = &mapclass->s_mapclass;
1036                 map.map_mname = mn;
1037                 map.map_mflags |= MF_FILECLASS;
1038
1039                 if (tTd(37, 5))
1040                         sm_dprintf("fileclass: F{%s}: map class %s, key %s, spec %s\n",
1041                                    mn, cl, key, spec);
1042
1043
1044                 /* parse map spec */
1045                 if (!map.map_class->map_parse(&map, spec))
1046                 {
1047                         /* map_parse() showed the error already */
1048                         sm_free(mn);
1049                         return;
1050                 }
1051                 map.map_mflags |= MF_VALID;
1052
1053                 /* open map */
1054                 if (map.map_class->map_open(&map, O_RDONLY))
1055                 {
1056                         map.map_mflags |= MF_OPEN;
1057                         map.map_pid = getpid();
1058                 }
1059                 else
1060                 {
1061                         if (!optional &&
1062                             !bitset(MF_OPTIONAL, map.map_mflags))
1063                                 syserr("fileclass: F{%s}: map open failed",
1064                                        mn);
1065                         sm_free(mn);
1066                         return;
1067                 }
1068
1069                 /* lookup */
1070                 p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
1071                 if (status != EX_OK && status != EX_NOTFOUND)
1072                 {
1073                         if (!optional)
1074                                 syserr("fileclass: F{%s}: map lookup failed",
1075                                        mn);
1076                         p = NULL;
1077                 }
1078
1079                 /* use the results */
1080                 if (p != NULL)
1081                         parse_class_words(class, p);
1082
1083                 /* close map */
1084                 map.map_mflags |= MF_CLOSING;
1085                 map.map_class->map_close(&map);
1086                 map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1087                 sm_free(mn);
1088                 return;
1089         }
1090         else if (filename[0] == '|')
1091         {
1092                 auto int fd;
1093                 int i;
1094                 char *argv[MAXPV + 1];
1095
1096                 i = 0;
1097                 for (p = strtok(&filename[1], " \t");
1098                      p != NULL && i < MAXPV;
1099                      p = strtok(NULL, " \t"))
1100                         argv[i++] = p;
1101                 argv[i] = NULL;
1102                 pid = prog_open(argv, &fd, CurEnv);
1103                 if (pid < 0)
1104                         f = NULL;
1105                 else
1106                         f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
1107                                        (void *) &fd, SM_IO_RDONLY, NULL);
1108         }
1109         else
1110         {
1111                 pid = -1;
1112                 sff = SFF_REGONLY;
1113                 if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
1114                         sff |= SFF_SAFEDIRPATH;
1115                 if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
1116                              DontBlameSendmail))
1117                         sff |= SFF_NOWLINK;
1118                 if (safe)
1119                         sff |= SFF_OPENASROOT;
1120                 else if (RealUid == 0)
1121                         sff |= SFF_ROOTOK;
1122                 if (DontLockReadFiles)
1123                         sff |= SFF_NOLOCK;
1124                 f = safefopen(filename, O_RDONLY, 0, sff);
1125         }
1126         if (f == NULL)
1127         {
1128                 if (!optional)
1129                         syserr("fileclass: cannot open '%s'", filename);
1130                 return;
1131         }
1132
1133         while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
1134         {
1135 #if SCANF
1136                 char wordbuf[MAXLINE + 1];
1137 #endif /* SCANF */
1138
1139                 if (buf[0] == '#')
1140                         continue;
1141 #if SCANF
1142                 if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
1143                         continue;
1144                 p = wordbuf;
1145 #else /* SCANF */
1146                 p = buf;
1147 #endif /* SCANF */
1148
1149                 parse_class_words(class, p);
1150
1151                 /*
1152                 **  If anything else is added here,
1153                 **  check if the '@' map case above
1154                 **  needs the code as well.
1155                 */
1156         }
1157
1158         (void) sm_io_close(f, SM_TIME_DEFAULT);
1159         if (pid > 0)
1160                 (void) waitfor(pid);
1161 }
1162 /*
1163 **  MAKEMAILER -- define a new mailer.
1164 **
1165 **      Parameters:
1166 **              line -- description of mailer.  This is in labeled
1167 **                      fields.  The fields are:
1168 **                         A -- the argv for this mailer
1169 **                         C -- the character set for MIME conversions
1170 **                         D -- the directory to run in
1171 **                         E -- the eol string
1172 **                         F -- the flags associated with the mailer
1173 **                         L -- the maximum line length
1174 **                         M -- the maximum message size
1175 **                         N -- the niceness at which to run
1176 **                         P -- the path to the mailer
1177 **                         Q -- the queue group for the mailer
1178 **                         R -- the recipient rewriting set
1179 **                         S -- the sender rewriting set
1180 **                         T -- the mailer type (for DSNs)
1181 **                         U -- the uid to run as
1182 **                         W -- the time to wait at the end
1183 **                         m -- maximum messages per connection
1184 **                         r -- maximum number of recipients per message
1185 **                         / -- new root directory
1186 **                      The first word is the canonical name of the mailer.
1187 **
1188 **      Returns:
1189 **              none.
1190 **
1191 **      Side Effects:
1192 **              enters the mailer into the mailer table.
1193 */
1194
1195 void
1196 makemailer(line)
1197         char *line;
1198 {
1199         register char *p;
1200         register struct mailer *m;
1201         register STAB *s;
1202         int i;
1203         char fcode;
1204         auto char *endp;
1205         static int nextmailer = 0;      /* "free" index into Mailer struct */
1206
1207         /* allocate a mailer and set up defaults */
1208         m = (struct mailer *) xalloc(sizeof(*m));
1209         memset((char *) m, '\0', sizeof(*m));
1210         errno = 0; /* avoid bogus error text */
1211
1212         /* collect the mailer name */
1213         for (p = line;
1214              *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
1215              p++)
1216                 continue;
1217         if (*p != '\0')
1218                 *p++ = '\0';
1219         if (line[0] == '\0')
1220         {
1221                 syserr("name required for mailer");
1222                 return;
1223         }
1224         m->m_name = newstr(line);
1225         m->m_qgrp = NOQGRP;
1226         m->m_uid = NO_UID;
1227         m->m_gid = NO_GID;
1228
1229         /* now scan through and assign info from the fields */
1230         while (*p != '\0')
1231         {
1232                 auto char *delimptr;
1233
1234                 while (*p != '\0' &&
1235                        (*p == ',' || (isascii(*p) && isspace(*p))))
1236                         p++;
1237
1238                 /* p now points to field code */
1239                 fcode = *p;
1240                 while (*p != '\0' && *p != '=' && *p != ',')
1241                         p++;
1242                 if (*p++ != '=')
1243                 {
1244                         syserr("mailer %s: `=' expected", m->m_name);
1245                         return;
1246                 }
1247                 while (isascii(*p) && isspace(*p))
1248                         p++;
1249
1250                 /* p now points to the field body */
1251                 p = munchstring(p, &delimptr, ',');
1252
1253                 /* install the field into the mailer struct */
1254                 switch (fcode)
1255                 {
1256                   case 'P':             /* pathname */
1257                         if (*p != '\0') /* error is issued below */
1258                                 m->m_mailer = newstr(p);
1259                         break;
1260
1261                   case 'F':             /* flags */
1262                         for (; *p != '\0'; p++)
1263                         {
1264                                 if (!(isascii(*p) && isspace(*p)))
1265                                 {
1266 #if _FFR_DEPRECATE_MAILER_FLAG_I
1267                                         if (*p == M_INTERNAL)
1268                                                 sm_syslog(LOG_WARNING, NOQID,
1269                                                           "WARNING: mailer=%s, flag=%c deprecated",
1270                                                           m->m_name, *p);
1271 #endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
1272                                         setbitn(bitidx(*p), m->m_flags);
1273                                 }
1274                         }
1275                         break;
1276
1277                   case 'S':             /* sender rewriting ruleset */
1278                   case 'R':             /* recipient rewriting ruleset */
1279                         i = strtorwset(p, &endp, ST_ENTER);
1280                         if (i < 0)
1281                                 return;
1282                         if (fcode == 'S')
1283                                 m->m_sh_rwset = m->m_se_rwset = i;
1284                         else
1285                                 m->m_rh_rwset = m->m_re_rwset = i;
1286
1287                         p = endp;
1288                         if (*p++ == '/')
1289                         {
1290                                 i = strtorwset(p, NULL, ST_ENTER);
1291                                 if (i < 0)
1292                                         return;
1293                                 if (fcode == 'S')
1294                                         m->m_sh_rwset = i;
1295                                 else
1296                                         m->m_rh_rwset = i;
1297                         }
1298                         break;
1299
1300                   case 'E':             /* end of line string */
1301                         if (*p == '\0')
1302                                 syserr("mailer %s: null end-of-line string",
1303                                         m->m_name);
1304                         else
1305                                 m->m_eol = newstr(p);
1306                         break;
1307
1308                   case 'A':             /* argument vector */
1309                         if (*p != '\0') /* error is issued below */
1310                                 m->m_argv = makeargv(p);
1311                         break;
1312
1313                   case 'M':             /* maximum message size */
1314                         m->m_maxsize = atol(p);
1315                         break;
1316
1317                   case 'm':             /* maximum messages per connection */
1318                         m->m_maxdeliveries = atoi(p);
1319                         break;
1320
1321                   case 'r':             /* max recipient per envelope */
1322                         m->m_maxrcpt = atoi(p);
1323                         break;
1324
1325                   case 'L':             /* maximum line length */
1326                         m->m_linelimit = atoi(p);
1327                         if (m->m_linelimit < 0)
1328                                 m->m_linelimit = 0;
1329                         break;
1330
1331                   case 'N':             /* run niceness */
1332                         m->m_nice = atoi(p);
1333                         break;
1334
1335                   case 'D':             /* working directory */
1336                         if (*p == '\0')
1337                                 syserr("mailer %s: null working directory",
1338                                         m->m_name);
1339                         else
1340                                 m->m_execdir = newstr(p);
1341                         break;
1342
1343                   case 'C':             /* default charset */
1344                         if (*p == '\0')
1345                                 syserr("mailer %s: null charset", m->m_name);
1346                         else
1347                                 m->m_defcharset = newstr(p);
1348                         break;
1349
1350                   case 'Q':             /* queue for this mailer */
1351                         if (*p == '\0')
1352                         {
1353                                 syserr("mailer %s: null queue", m->m_name);
1354                                 break;
1355                         }
1356                         s = stab(p, ST_QUEUE, ST_FIND);
1357                         if (s == NULL)
1358                                 syserr("mailer %s: unknown queue %s",
1359                                         m->m_name, p);
1360                         else
1361                                 m->m_qgrp = s->s_quegrp->qg_index;
1362                         break;
1363
1364                   case 'T':             /* MTA-Name/Address/Diagnostic types */
1365                         /* extract MTA name type; default to "dns" */
1366                         m->m_mtatype = newstr(p);
1367                         p = strchr(m->m_mtatype, '/');
1368                         if (p != NULL)
1369                         {
1370                                 *p++ = '\0';
1371                                 if (*p == '\0')
1372                                         p = NULL;
1373                         }
1374                         if (*m->m_mtatype == '\0')
1375                                 m->m_mtatype = "dns";
1376
1377                         /* extract address type; default to "rfc822" */
1378                         m->m_addrtype = p;
1379                         if (p != NULL)
1380                                 p = strchr(p, '/');
1381                         if (p != NULL)
1382                         {
1383                                 *p++ = '\0';
1384                                 if (*p == '\0')
1385                                         p = NULL;
1386                         }
1387                         if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1388                                 m->m_addrtype = "rfc822";
1389
1390                         /* extract diagnostic type; default to "smtp" */
1391                         m->m_diagtype = p;
1392                         if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1393                                 m->m_diagtype = "smtp";
1394                         break;
1395
1396                   case 'U':             /* user id */
1397                         if (isascii(*p) && !isdigit(*p))
1398                         {
1399                                 char *q = p;
1400                                 struct passwd *pw;
1401
1402                                 while (*p != '\0' && isascii(*p) &&
1403                                        (isalnum(*p) || strchr("-_", *p) != NULL))
1404                                         p++;
1405                                 while (isascii(*p) && isspace(*p))
1406                                         *p++ = '\0';
1407                                 if (*p != '\0')
1408                                         *p++ = '\0';
1409                                 if (*q == '\0')
1410                                 {
1411                                         syserr("mailer %s: null user name",
1412                                                 m->m_name);
1413                                         break;
1414                                 }
1415                                 pw = sm_getpwnam(q);
1416                                 if (pw == NULL)
1417                                 {
1418                                         syserr("readcf: mailer U= flag: unknown user %s", q);
1419                                         break;
1420                                 }
1421                                 else
1422                                 {
1423                                         m->m_uid = pw->pw_uid;
1424                                         m->m_gid = pw->pw_gid;
1425                                 }
1426                         }
1427                         else
1428                         {
1429                                 auto char *q;
1430
1431                                 m->m_uid = strtol(p, &q, 0);
1432                                 p = q;
1433                                 while (isascii(*p) && isspace(*p))
1434                                         p++;
1435                                 if (*p != '\0')
1436                                         p++;
1437                         }
1438                         while (isascii(*p) && isspace(*p))
1439                                 p++;
1440                         if (*p == '\0')
1441                                 break;
1442                         if (isascii(*p) && !isdigit(*p))
1443                         {
1444                                 char *q = p;
1445                                 struct group *gr;
1446
1447                                 while (isascii(*p) && isalnum(*p))
1448                                         p++;
1449                                 *p++ = '\0';
1450                                 if (*q == '\0')
1451                                 {
1452                                         syserr("mailer %s: null group name",
1453                                                 m->m_name);
1454                                         break;
1455                                 }
1456                                 gr = getgrnam(q);
1457                                 if (gr == NULL)
1458                                 {
1459                                         syserr("readcf: mailer U= flag: unknown group %s", q);
1460                                         break;
1461                                 }
1462                                 else
1463                                         m->m_gid = gr->gr_gid;
1464                         }
1465                         else
1466                         {
1467                                 m->m_gid = strtol(p, NULL, 0);
1468                         }
1469                         break;
1470
1471                   case 'W':             /* wait timeout */
1472                         m->m_wait = convtime(p, 's');
1473                         break;
1474
1475                   case '/':             /* new root directory */
1476                         if (*p == '\0')
1477                                 syserr("mailer %s: null root directory",
1478                                         m->m_name);
1479                         else
1480                                 m->m_rootdir = newstr(p);
1481                         break;
1482
1483                   default:
1484                         syserr("M%s: unknown mailer equate %c=",
1485                                m->m_name, fcode);
1486                         break;
1487                 }
1488
1489                 p = delimptr;
1490         }
1491
1492 #if !HASRRESVPORT
1493         if (bitnset(M_SECURE_PORT, m->m_flags))
1494         {
1495                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1496                                      "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
1497                                      m->m_name, M_SECURE_PORT);
1498         }
1499 #endif /* !HASRRESVPORT */
1500
1501 #if !HASNICE
1502         if (m->m_nice != 0)
1503         {
1504                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1505                                      "M%s: Warning: N= set on system that doesn't support nice()\n",
1506                                      m->m_name);
1507         }
1508 #endif /* !HASNICE */
1509
1510         /* do some rationality checking */
1511         if (m->m_argv == NULL)
1512         {
1513                 syserr("M%s: A= argument required", m->m_name);
1514                 return;
1515         }
1516         if (m->m_mailer == NULL)
1517         {
1518                 syserr("M%s: P= argument required", m->m_name);
1519                 return;
1520         }
1521
1522         if (nextmailer >= MAXMAILERS)
1523         {
1524                 syserr("too many mailers defined (%d max)", MAXMAILERS);
1525                 return;
1526         }
1527
1528         if (m->m_maxrcpt <= 0)
1529                 m->m_maxrcpt = DEFAULT_MAX_RCPT;
1530
1531         /* do some heuristic cleanup for back compatibility */
1532         if (bitnset(M_LIMITS, m->m_flags))
1533         {
1534                 if (m->m_linelimit == 0)
1535                         m->m_linelimit = SMTPLINELIM;
1536                 if (ConfigLevel < 2)
1537                         setbitn(M_7BITS, m->m_flags);
1538         }
1539
1540         if (strcmp(m->m_mailer, "[TCP]") == 0)
1541         {
1542                 syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
1543                 return;
1544         }
1545
1546         if (strcmp(m->m_mailer, "[IPC]") == 0)
1547         {
1548                 /* Use the second argument for host or path to socket */
1549                 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1550                     m->m_argv[1][0] == '\0')
1551                 {
1552                         syserr("M%s: too few parameters for %s mailer",
1553                                m->m_name, m->m_mailer);
1554                         return;
1555                 }
1556                 if (strcmp(m->m_argv[0], "TCP") != 0
1557 #if NETUNIX
1558                     && strcmp(m->m_argv[0], "FILE") != 0
1559 #endif /* NETUNIX */
1560                     )
1561                 {
1562                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1563                                              "M%s: Warning: first argument in %s mailer must be %s\n",
1564                                              m->m_name, m->m_mailer,
1565 #if NETUNIX
1566                                              "TCP or FILE"
1567 #else /* NETUNIX */
1568                                              "TCP"
1569 #endif /* NETUNIX */
1570                                      );
1571                 }
1572                 if (m->m_mtatype == NULL)
1573                         m->m_mtatype = "dns";
1574                 if (m->m_addrtype == NULL)
1575                         m->m_addrtype = "rfc822";
1576                 if (m->m_diagtype == NULL)
1577                 {
1578                         if (m->m_argv[0] != NULL &&
1579                             strcmp(m->m_argv[0], "FILE") == 0)
1580                                 m->m_diagtype = "x-unix";
1581                         else
1582                                 m->m_diagtype = "smtp";
1583                 }
1584         }
1585         else if (strcmp(m->m_mailer, "[FILE]") == 0)
1586         {
1587                 /* Use the second argument for filename */
1588                 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1589                     m->m_argv[2] != NULL)
1590                 {
1591                         syserr("M%s: too %s parameters for [FILE] mailer",
1592                                m->m_name,
1593                                (m->m_argv[0] == NULL ||
1594                                 m->m_argv[1] == NULL) ? "few" : "many");
1595                         return;
1596                 }
1597                 else if (strcmp(m->m_argv[0], "FILE") != 0)
1598                 {
1599                         syserr("M%s: first argument in [FILE] mailer must be FILE",
1600                                m->m_name);
1601                         return;
1602                 }
1603         }
1604
1605         if (m->m_eol == NULL)
1606         {
1607                 char **pp;
1608
1609                 /* default for SMTP is \r\n; use \n for local delivery */
1610                 for (pp = m->m_argv; *pp != NULL; pp++)
1611                 {
1612                         for (p = *pp; *p != '\0'; )
1613                         {
1614                                 if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1615                                         break;
1616                         }
1617                         if (*p != '\0')
1618                                 break;
1619                 }
1620                 if (*pp == NULL)
1621                         m->m_eol = "\r\n";
1622                 else
1623                         m->m_eol = "\n";
1624         }
1625
1626         /* enter the mailer into the symbol table */
1627         s = stab(m->m_name, ST_MAILER, ST_ENTER);
1628         if (s->s_mailer != NULL)
1629         {
1630                 i = s->s_mailer->m_mno;
1631                 sm_free(s->s_mailer); /* XXX */
1632         }
1633         else
1634         {
1635                 i = nextmailer++;
1636         }
1637         Mailer[i] = s->s_mailer = m;
1638         m->m_mno = i;
1639 }
1640 /*
1641 **  MUNCHSTRING -- translate a string into internal form.
1642 **
1643 **      Parameters:
1644 **              p -- the string to munch.
1645 **              delimptr -- if non-NULL, set to the pointer of the
1646 **                      field delimiter character.
1647 **              delim -- the delimiter for the field.
1648 **
1649 **      Returns:
1650 **              the munched string.
1651 **
1652 **      Side Effects:
1653 **              the munched string is a local static buffer.
1654 **              it must be copied before the function is called again.
1655 */
1656
1657 char *
1658 munchstring(p, delimptr, delim)
1659         register char *p;
1660         char **delimptr;
1661         int delim;
1662 {
1663         register char *q;
1664         bool backslash = false;
1665         bool quotemode = false;
1666         static char buf[MAXLINE];
1667
1668         for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1669         {
1670                 if (backslash)
1671                 {
1672                         /* everything is roughly literal */
1673                         backslash = false;
1674                         switch (*p)
1675                         {
1676                           case 'r':             /* carriage return */
1677                                 *q++ = '\r';
1678                                 continue;
1679
1680                           case 'n':             /* newline */
1681                                 *q++ = '\n';
1682                                 continue;
1683
1684                           case 'f':             /* form feed */
1685                                 *q++ = '\f';
1686                                 continue;
1687
1688                           case 'b':             /* backspace */
1689                                 *q++ = '\b';
1690                                 continue;
1691                         }
1692                         *q++ = *p;
1693                 }
1694                 else
1695                 {
1696                         if (*p == '\\')
1697                                 backslash = true;
1698                         else if (*p == '"')
1699                                 quotemode = !quotemode;
1700                         else if (quotemode || *p != delim)
1701                                 *q++ = *p;
1702                         else
1703                                 break;
1704                 }
1705         }
1706
1707         if (delimptr != NULL)
1708                 *delimptr = p;
1709         *q++ = '\0';
1710         return buf;
1711 }
1712 /*
1713 **  EXTRQUOTSTR -- extract a (quoted) string.
1714 **
1715 **      This routine deals with quoted (") strings and escaped
1716 **      spaces (\\ ).
1717 **
1718 **      Parameters:
1719 **              p -- source string.
1720 **              delimptr -- if non-NULL, set to the pointer of the
1721 **                      field delimiter character.
1722 **              delimbuf -- delimiters for the field.
1723 **              st -- if non-NULL, store the return value (whether the
1724 **                      string was correctly quoted) here.
1725 **
1726 **      Returns:
1727 **              the extracted string.
1728 **
1729 **      Side Effects:
1730 **              the returned string is a local static buffer.
1731 **              it must be copied before the function is called again.
1732 */
1733
1734 static char *
1735 extrquotstr(p, delimptr, delimbuf, st)
1736         register char *p;
1737         char **delimptr;
1738         char *delimbuf;
1739         bool *st;
1740 {
1741         register char *q;
1742         bool backslash = false;
1743         bool quotemode = false;
1744         static char buf[MAXLINE];
1745
1746         for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1747         {
1748                 if (backslash)
1749                 {
1750                         backslash = false;
1751                         if (*p != ' ')
1752                                 *q++ = '\\';
1753                 }
1754                 if (*p == '\\')
1755                         backslash = true;
1756                 else if (*p == '"')
1757                         quotemode = !quotemode;
1758                 else if (quotemode ||
1759                          strchr(delimbuf, (int) *p) == NULL)
1760                         *q++ = *p;
1761                 else
1762                         break;
1763         }
1764
1765         if (delimptr != NULL)
1766                 *delimptr = p;
1767         *q++ = '\0';
1768         if (st != NULL)
1769                 *st = !(quotemode || backslash);
1770         return buf;
1771 }
1772 /*
1773 **  MAKEARGV -- break up a string into words
1774 **
1775 **      Parameters:
1776 **              p -- the string to break up.
1777 **
1778 **      Returns:
1779 **              a char **argv (dynamically allocated)
1780 **
1781 **      Side Effects:
1782 **              munges p.
1783 */
1784
1785 static char **
1786 makeargv(p)
1787         register char *p;
1788 {
1789         char *q;
1790         int i;
1791         char **avp;
1792         char *argv[MAXPV + 1];
1793
1794         /* take apart the words */
1795         i = 0;
1796         while (*p != '\0' && i < MAXPV)
1797         {
1798                 q = p;
1799                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1800                         p++;
1801                 while (isascii(*p) && isspace(*p))
1802                         *p++ = '\0';
1803                 argv[i++] = newstr(q);
1804         }
1805         argv[i++] = NULL;
1806
1807         /* now make a copy of the argv */
1808         avp = (char **) xalloc(sizeof(*avp) * i);
1809         memmove((char *) avp, (char *) argv, sizeof(*avp) * i);
1810
1811         return avp;
1812 }
1813 /*
1814 **  PRINTRULES -- print rewrite rules (for debugging)
1815 **
1816 **      Parameters:
1817 **              none.
1818 **
1819 **      Returns:
1820 **              none.
1821 **
1822 **      Side Effects:
1823 **              prints rewrite rules.
1824 */
1825
1826 void
1827 printrules()
1828 {
1829         register struct rewrite *rwp;
1830         register int ruleset;
1831
1832         for (ruleset = 0; ruleset < 10; ruleset++)
1833         {
1834                 if (RewriteRules[ruleset] == NULL)
1835                         continue;
1836                 sm_dprintf("\n----Rule Set %d:", ruleset);
1837
1838                 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1839                 {
1840                         sm_dprintf("\nLHS:");
1841                         printav(sm_debug_file(), rwp->r_lhs);
1842                         sm_dprintf("RHS:");
1843                         printav(sm_debug_file(), rwp->r_rhs);
1844                 }
1845         }
1846 }
1847 /*
1848 **  PRINTMAILER -- print mailer structure (for debugging)
1849 **
1850 **      Parameters:
1851 **              fp -- output file
1852 **              m -- the mailer to print
1853 **
1854 **      Returns:
1855 **              none.
1856 */
1857
1858 void
1859 printmailer(fp, m)
1860         SM_FILE_T *fp;
1861         register MAILER *m;
1862 {
1863         int j;
1864
1865         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
1866                              "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
1867                              m->m_mailer);
1868         if (RuleSetNames[m->m_se_rwset] == NULL)
1869                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
1870                                      m->m_se_rwset);
1871         else
1872                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
1873                                      RuleSetNames[m->m_se_rwset]);
1874         if (RuleSetNames[m->m_sh_rwset] == NULL)
1875                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d R=",
1876                                      m->m_sh_rwset);
1877         else
1878                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s R=",
1879                                      RuleSetNames[m->m_sh_rwset]);
1880         if (RuleSetNames[m->m_re_rwset] == NULL)
1881                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
1882                                      m->m_re_rwset);
1883         else
1884                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
1885                                      RuleSetNames[m->m_re_rwset]);
1886         if (RuleSetNames[m->m_rh_rwset] == NULL)
1887                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d ",
1888                                      m->m_rh_rwset);
1889         else
1890                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s ",
1891                                      RuleSetNames[m->m_rh_rwset]);
1892         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
1893                              m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
1894         for (j = '\0'; j <= '\177'; j++)
1895                 if (bitnset(j, m->m_flags))
1896                         (void) sm_io_putc(fp, SM_TIME_DEFAULT, j);
1897         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " L=%d E=",
1898                              m->m_linelimit);
1899         xputs(fp, m->m_eol);
1900         if (m->m_defcharset != NULL)
1901                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " C=%s",
1902                                      m->m_defcharset);
1903         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " T=%s/%s/%s",
1904                              m->m_mtatype == NULL
1905                                 ? "<undefined>" : m->m_mtatype,
1906                              m->m_addrtype == NULL
1907                                 ? "<undefined>" : m->m_addrtype,
1908                              m->m_diagtype == NULL
1909                                 ? "<undefined>" : m->m_diagtype);
1910         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
1911         if (m->m_argv != NULL)
1912         {
1913                 char **a = m->m_argv;
1914
1915                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " A=");
1916                 while (*a != NULL)
1917                 {
1918                         if (a != m->m_argv)
1919                                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
1920                                                      " ");
1921                         xputs(fp, *a++);
1922                 }
1923         }
1924         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
1925 }
1926 /*
1927 **  SETOPTION -- set global processing option
1928 **
1929 **      Parameters:
1930 **              opt -- option name.
1931 **              val -- option value (as a text string).
1932 **              safe -- set if this came from a configuration file.
1933 **                      Some options (if set from the command line) will
1934 **                      reset the user id to avoid security problems.
1935 **              sticky -- if set, don't let other setoptions override
1936 **                      this value.
1937 **              e -- the main envelope.
1938 **
1939 **      Returns:
1940 **              none.
1941 **
1942 **      Side Effects:
1943 **              Sets options as implied by the arguments.
1944 */
1945
1946 static BITMAP256        StickyOpt;              /* set if option is stuck */
1947
1948 #if NAMED_BIND
1949
1950 static struct resolverflags
1951 {
1952         char    *rf_name;       /* name of the flag */
1953         long    rf_bits;        /* bits to set/clear */
1954 } ResolverFlags[] =
1955 {
1956         { "debug",      RES_DEBUG       },
1957         { "aaonly",     RES_AAONLY      },
1958         { "usevc",      RES_USEVC       },
1959         { "primary",    RES_PRIMARY     },
1960         { "igntc",      RES_IGNTC       },
1961         { "recurse",    RES_RECURSE     },
1962         { "defnames",   RES_DEFNAMES    },
1963         { "stayopen",   RES_STAYOPEN    },
1964         { "dnsrch",     RES_DNSRCH      },
1965 # ifdef RES_USE_INET6
1966         { "use_inet6",  RES_USE_INET6   },
1967 # endif /* RES_USE_INET6 */
1968         { "true",       0               },      /* avoid error on old syntax */
1969         { NULL,         0               }
1970 };
1971
1972 #endif /* NAMED_BIND */
1973
1974 #define OI_NONE         0       /* no special treatment */
1975 #define OI_SAFE         0x0001  /* safe for random people to use */
1976 #define OI_SUBOPT       0x0002  /* option has suboptions */
1977
1978 static struct optioninfo
1979 {
1980         char            *o_name;        /* long name of option */
1981         unsigned char   o_code;         /* short name of option */
1982         unsigned short  o_flags;        /* option flags */
1983 } OptionTab[] =
1984 {
1985 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
1986         { "RemoteMode",                 '>',            OI_NONE },
1987 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */
1988         { "SevenBitInput",              '7',            OI_SAFE },
1989         { "EightBitMode",               '8',            OI_SAFE },
1990         { "AliasFile",                  'A',            OI_NONE },
1991         { "AliasWait",                  'a',            OI_NONE },
1992         { "BlankSub",                   'B',            OI_NONE },
1993         { "MinFreeBlocks",              'b',            OI_SAFE },
1994         { "CheckpointInterval",         'C',            OI_SAFE },
1995         { "HoldExpensive",              'c',            OI_NONE },
1996         { "DeliveryMode",               'd',            OI_SAFE },
1997         { "ErrorHeader",                'E',            OI_NONE },
1998         { "ErrorMode",                  'e',            OI_SAFE },
1999         { "TempFileMode",               'F',            OI_NONE },
2000         { "SaveFromLine",               'f',            OI_NONE },
2001         { "MatchGECOS",                 'G',            OI_NONE },
2002
2003         /* no long name, just here to avoid problems in setoption */
2004         { "",                           'g',            OI_NONE },
2005         { "HelpFile",                   'H',            OI_NONE },
2006         { "MaxHopCount",                'h',            OI_NONE },
2007         { "ResolverOptions",            'I',            OI_NONE },
2008         { "IgnoreDots",                 'i',            OI_SAFE },
2009         { "ForwardPath",                'J',            OI_NONE },
2010         { "SendMimeErrors",             'j',            OI_SAFE },
2011         { "ConnectionCacheSize",        'k',            OI_NONE },
2012         { "ConnectionCacheTimeout",     'K',            OI_NONE },
2013         { "UseErrorsTo",                'l',            OI_NONE },
2014         { "LogLevel",                   'L',            OI_SAFE },
2015         { "MeToo",                      'm',            OI_SAFE },
2016
2017         /* no long name, just here to avoid problems in setoption */
2018         { "",                           'M',            OI_NONE },
2019         { "CheckAliases",               'n',            OI_NONE },
2020         { "OldStyleHeaders",            'o',            OI_SAFE },
2021         { "DaemonPortOptions",          'O',            OI_NONE },
2022         { "PrivacyOptions",             'p',            OI_SAFE },
2023         { "PostmasterCopy",             'P',            OI_NONE },
2024         { "QueueFactor",                'q',            OI_NONE },
2025         { "QueueDirectory",             'Q',            OI_NONE },
2026         { "DontPruneRoutes",            'R',            OI_NONE },
2027         { "Timeout",                    'r',            OI_SUBOPT },
2028         { "StatusFile",                 'S',            OI_NONE },
2029         { "SuperSafe",                  's',            OI_SAFE },
2030         { "QueueTimeout",               'T',            OI_NONE },
2031         { "TimeZoneSpec",               't',            OI_NONE },
2032         { "UserDatabaseSpec",           'U',            OI_NONE },
2033         { "DefaultUser",                'u',            OI_NONE },
2034         { "FallbackMXhost",             'V',            OI_NONE },
2035         { "Verbose",                    'v',            OI_SAFE },
2036         { "TryNullMXList",              'w',            OI_NONE },
2037         { "QueueLA",                    'x',            OI_NONE },
2038         { "RefuseLA",                   'X',            OI_NONE },
2039         { "RecipientFactor",            'y',            OI_NONE },
2040         { "ForkEachJob",                'Y',            OI_NONE },
2041         { "ClassFactor",                'z',            OI_NONE },
2042         { "RetryFactor",                'Z',            OI_NONE },
2043 #define O_QUEUESORTORD  0x81
2044         { "QueueSortOrder",             O_QUEUESORTORD, OI_SAFE },
2045 #define O_HOSTSFILE     0x82
2046         { "HostsFile",                  O_HOSTSFILE,    OI_NONE },
2047 #define O_MQA           0x83
2048         { "MinQueueAge",                O_MQA,          OI_SAFE },
2049 #define O_DEFCHARSET    0x85
2050         { "DefaultCharSet",             O_DEFCHARSET,   OI_SAFE },
2051 #define O_SSFILE        0x86
2052         { "ServiceSwitchFile",          O_SSFILE,       OI_NONE },
2053 #define O_DIALDELAY     0x87
2054         { "DialDelay",                  O_DIALDELAY,    OI_SAFE },
2055 #define O_NORCPTACTION  0x88
2056         { "NoRecipientAction",          O_NORCPTACTION, OI_SAFE },
2057 #define O_SAFEFILEENV   0x89
2058         { "SafeFileEnvironment",        O_SAFEFILEENV,  OI_NONE },
2059 #define O_MAXMSGSIZE    0x8a
2060         { "MaxMessageSize",             O_MAXMSGSIZE,   OI_NONE },
2061 #define O_COLONOKINADDR 0x8b
2062         { "ColonOkInAddr",              O_COLONOKINADDR, OI_SAFE },
2063 #define O_MAXQUEUERUN   0x8c
2064         { "MaxQueueRunSize",            O_MAXQUEUERUN,  OI_SAFE },
2065 #define O_MAXCHILDREN   0x8d
2066         { "MaxDaemonChildren",          O_MAXCHILDREN,  OI_NONE },
2067 #define O_KEEPCNAMES    0x8e
2068         { "DontExpandCnames",           O_KEEPCNAMES,   OI_NONE },
2069 #define O_MUSTQUOTE     0x8f
2070         { "MustQuoteChars",             O_MUSTQUOTE,    OI_NONE },
2071 #define O_SMTPGREETING  0x90
2072         { "SmtpGreetingMessage",        O_SMTPGREETING, OI_NONE },
2073 #define O_UNIXFROM      0x91
2074         { "UnixFromLine",               O_UNIXFROM,     OI_NONE },
2075 #define O_OPCHARS       0x92
2076         { "OperatorChars",              O_OPCHARS,      OI_NONE },
2077 #define O_DONTINITGRPS  0x93
2078         { "DontInitGroups",             O_DONTINITGRPS, OI_NONE },
2079 #define O_SLFH          0x94
2080         { "SingleLineFromHeader",       O_SLFH,         OI_SAFE },
2081 #define O_ABH           0x95
2082         { "AllowBogusHELO",             O_ABH,          OI_SAFE },
2083 #define O_CONNTHROT     0x97
2084         { "ConnectionRateThrottle",     O_CONNTHROT,    OI_NONE },
2085 #define O_UGW           0x99
2086         { "UnsafeGroupWrites",          O_UGW,          OI_NONE },
2087 #define O_DBLBOUNCE     0x9a
2088         { "DoubleBounceAddress",        O_DBLBOUNCE,    OI_NONE },
2089 #define O_HSDIR         0x9b
2090         { "HostStatusDirectory",        O_HSDIR,        OI_NONE },
2091 #define O_SINGTHREAD    0x9c
2092         { "SingleThreadDelivery",       O_SINGTHREAD,   OI_NONE },
2093 #define O_RUNASUSER     0x9d
2094         { "RunAsUser",                  O_RUNASUSER,    OI_NONE },
2095 #define O_DSN_RRT       0x9e
2096         { "RrtImpliesDsn",              O_DSN_RRT,      OI_NONE },
2097 #define O_PIDFILE       0x9f
2098         { "PidFile",                    O_PIDFILE,      OI_NONE },
2099 #define O_DONTBLAMESENDMAIL     0xa0
2100         { "DontBlameSendmail",          O_DONTBLAMESENDMAIL,    OI_NONE },
2101 #define O_DPI           0xa1
2102         { "DontProbeInterfaces",        O_DPI,          OI_NONE },
2103 #define O_MAXRCPT       0xa2
2104         { "MaxRecipientsPerMessage",    O_MAXRCPT,      OI_SAFE },
2105 #define O_DEADLETTER    0xa3
2106         { "DeadLetterDrop",             O_DEADLETTER,   OI_NONE },
2107 #if _FFR_DONTLOCKFILESFORREAD_OPTION
2108 # define O_DONTLOCK     0xa4
2109         { "DontLockFilesForRead",       O_DONTLOCK,     OI_NONE },
2110 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
2111 #define O_MAXALIASRCSN  0xa5
2112         { "MaxAliasRecursion",          O_MAXALIASRCSN, OI_NONE },
2113 #define O_CNCTONLYTO    0xa6
2114         { "ConnectOnlyTo",              O_CNCTONLYTO,   OI_NONE },
2115 #define O_TRUSTUSER     0xa7
2116         { "TrustedUser",                O_TRUSTUSER,    OI_NONE },
2117 #define O_MAXMIMEHDRLEN 0xa8
2118         { "MaxMimeHeaderLength",        O_MAXMIMEHDRLEN,        OI_NONE },
2119 #define O_CONTROLSOCKET 0xa9
2120         { "ControlSocketName",          O_CONTROLSOCKET,        OI_NONE },
2121 #define O_MAXHDRSLEN    0xaa
2122         { "MaxHeadersLength",           O_MAXHDRSLEN,   OI_NONE },
2123 #if _FFR_MAX_FORWARD_ENTRIES
2124 # define O_MAXFORWARD   0xab
2125         { "MaxForwardEntries",          O_MAXFORWARD,   OI_NONE },
2126 #endif /* _FFR_MAX_FORWARD_ENTRIES */
2127 #define O_PROCTITLEPREFIX       0xac
2128         { "ProcessTitlePrefix",         O_PROCTITLEPREFIX,      OI_NONE },
2129 #define O_SASLINFO      0xad
2130 #if _FFR_ALLOW_SASLINFO
2131         { "DefaultAuthInfo",            O_SASLINFO,     OI_SAFE },
2132 #else /* _FFR_ALLOW_SASLINFO */
2133         { "DefaultAuthInfo",            O_SASLINFO,     OI_NONE },
2134 #endif /* _FFR_ALLOW_SASLINFO */
2135 #define O_SASLMECH      0xae
2136         { "AuthMechanisms",             O_SASLMECH,     OI_NONE },
2137 #define O_CLIENTPORT    0xaf
2138         { "ClientPortOptions",          O_CLIENTPORT,   OI_NONE },
2139 #define O_DF_BUFSIZE    0xb0
2140         { "DataFileBufferSize",         O_DF_BUFSIZE,   OI_NONE },
2141 #define O_XF_BUFSIZE    0xb1
2142         { "XscriptFileBufferSize",      O_XF_BUFSIZE,   OI_NONE },
2143 #define O_LDAPDEFAULTSPEC       0xb2
2144         { "LDAPDefaultSpec",            O_LDAPDEFAULTSPEC,      OI_NONE },
2145 #define O_SRVCERTFILE   0xb4
2146         { "ServerCertFile",             O_SRVCERTFILE,  OI_NONE },
2147 #define O_SRVKEYFILE    0xb5
2148         { "ServerKeyFile",              O_SRVKEYFILE,   OI_NONE },
2149 #define O_CLTCERTFILE   0xb6
2150         { "ClientCertFile",             O_CLTCERTFILE,  OI_NONE },
2151 #define O_CLTKEYFILE    0xb7
2152         { "ClientKeyFile",              O_CLTKEYFILE,   OI_NONE },
2153 #define O_CACERTFILE    0xb8
2154         { "CACertFile",                 O_CACERTFILE,   OI_NONE },
2155 #define O_CACERTPATH    0xb9
2156         { "CACertPath",                 O_CACERTPATH,   OI_NONE },
2157 #define O_DHPARAMS      0xba
2158         { "DHParameters",               O_DHPARAMS,     OI_NONE },
2159 #define O_INPUTMILTER   0xbb
2160         { "InputMailFilters",           O_INPUTMILTER,  OI_NONE },
2161 #define O_MILTER        0xbc
2162         { "Milter",                     O_MILTER,       OI_SUBOPT       },
2163 #define O_SASLOPTS      0xbd
2164         { "AuthOptions",                O_SASLOPTS,     OI_NONE },
2165 #define O_QUEUE_FILE_MODE       0xbe
2166         { "QueueFileMode",              O_QUEUE_FILE_MODE, OI_NONE      },
2167 #if _FFR_TLS_1
2168 # define O_DHPARAMS5    0xbf
2169         { "DHParameters512",            O_DHPARAMS5,    OI_NONE },
2170 # define O_CIPHERLIST   0xc0
2171         { "CipherList",                 O_CIPHERLIST,   OI_NONE },
2172 #endif /* _FFR_TLS_1 */
2173 #define O_RANDFILE      0xc1
2174         { "RandFile",                   O_RANDFILE,     OI_NONE },
2175 #define O_TLS_SRV_OPTS  0xc2
2176         { "TLSSrvOptions",              O_TLS_SRV_OPTS, OI_NONE },
2177 #define O_RCPTTHROT     0xc3
2178         { "BadRcptThrottle",            O_RCPTTHROT,    OI_SAFE },
2179 #define O_DLVR_MIN      0xc4
2180         { "DeliverByMin",               O_DLVR_MIN,     OI_NONE },
2181 #define O_MAXQUEUECHILDREN      0xc5
2182         { "MaxQueueChildren",           O_MAXQUEUECHILDREN,     OI_NONE },
2183 #define O_MAXRUNNERSPERQUEUE    0xc6
2184         { "MaxRunnersPerQueue",         O_MAXRUNNERSPERQUEUE,   OI_NONE },
2185 #define O_DIRECTSUBMODIFIERS    0xc7
2186         { "DirectSubmissionModifiers",  O_DIRECTSUBMODIFIERS,   OI_NONE },
2187 #define O_NICEQUEUERUN  0xc8
2188         { "NiceQueueRun",               O_NICEQUEUERUN, OI_NONE },
2189 #define O_SHMKEY        0xc9
2190         { "SharedMemoryKey",            O_SHMKEY,       OI_NONE },
2191 #define O_SASLBITS      0xca
2192         { "AuthMaxBits",                O_SASLBITS,     OI_NONE },
2193 #define O_MBDB          0xcb
2194         { "MailboxDatabase",            O_MBDB,         OI_NONE },
2195 #define O_MSQ           0xcc
2196         { "UseMSP",     O_MSQ,          OI_NONE },
2197 #define O_DELAY_LA      0xcd
2198         { "DelayLA",    O_DELAY_LA,     OI_NONE },
2199 #define O_FASTSPLIT     0xce
2200         { "FastSplit",  O_FASTSPLIT,    OI_NONE },
2201 #define O_SOFTBOUNCE    0xcf
2202         { "SoftBounce", O_SOFTBOUNCE,   OI_NONE },
2203 #define O_SHMKEYFILE    0xd0
2204         { "SharedMemoryKeyFile",        O_SHMKEYFILE,   OI_NONE },
2205 #define O_REJECTLOGINTERVAL     0xd1
2206         { "RejectLogInterval",  O_REJECTLOGINTERVAL,    OI_NONE },
2207 #define O_REQUIRES_DIR_FSYNC    0xd2
2208         { "RequiresDirfsync",   O_REQUIRES_DIR_FSYNC,   OI_NONE },
2209 #define O_CONNECTION_RATE_WINDOW_SIZE   0xd3
2210         { "ConnectionRateWindowSize", O_CONNECTION_RATE_WINDOW_SIZE, OI_NONE },
2211 #define O_CRLFILE       0xd4
2212         { "CRLFile",            O_CRLFILE,      OI_NONE },
2213 #define O_FALLBACKSMARTHOST     0xd5
2214         { "FallbackSmartHost",          O_FALLBACKSMARTHOST,    OI_NONE },
2215 #define O_SASLREALM     0xd6
2216         { "AuthRealm",          O_SASLREALM,    OI_NONE },
2217 #if _FFR_CRLPATH
2218 # define O_CRLPATH      0xd7
2219         { "CRLPath",            O_CRLPATH,      OI_NONE },
2220 #endif /* _FFR_CRLPATH */
2221 #define O_HELONAME 0xd8
2222         { "HeloName",   O_HELONAME,     OI_NONE },
2223 #if _FFR_MEMSTAT
2224 # define O_REFUSELOWMEM 0xd9
2225         { "RefuseLowMem",       O_REFUSELOWMEM, OI_NONE },
2226 # define O_QUEUELOWMEM  0xda
2227         { "QueueLowMem",        O_QUEUELOWMEM,  OI_NONE },
2228 # define O_MEMRESOURCE  0xdb
2229         { "MemoryResource",     O_MEMRESOURCE,  OI_NONE },
2230 #endif /* _FFR_MEMSTAT */
2231 #define O_MAXNOOPCOMMANDS 0xdc
2232         { "MaxNOOPCommands",    O_MAXNOOPCOMMANDS,      OI_NONE },
2233 #if _FFR_MSG_ACCEPT
2234 # define O_MSG_ACCEPT 0xdd
2235         { "MessageAccept",      O_MSG_ACCEPT,   OI_NONE },
2236 #endif /* _FFR_MSG_ACCEPT */
2237 #if _FFR_QUEUE_RUN_PARANOIA
2238 # define O_CHK_Q_RUNNERS 0xde
2239         { "CheckQueueRunners",  O_CHK_Q_RUNNERS,        OI_NONE },
2240 #endif /* _FFR_QUEUE_RUN_PARANOIA */
2241 #if _FFR_EIGHT_BIT_ADDR_OK
2242 # if !ALLOW_255
2243 #  ERROR FFR_EIGHT_BIT_ADDR_OK requires _ALLOW_255
2244 # endif /* !ALLOW_255 */
2245 # define O_EIGHT_BIT_ADDR_OK    0xdf
2246         { "EightBitAddrOK",     O_EIGHT_BIT_ADDR_OK,    OI_NONE },
2247 #endif /* _FFR_EIGHT_BIT_ADDR_OK */
2248 #if _FFR_ADDR_TYPE_MODES
2249 # define O_ADDR_TYPE_MODES      0xe0
2250         { "AddrTypeModes",      O_ADDR_TYPE_MODES,      OI_NONE },
2251 #endif /* _FFR_ADDR_TYPE_MODES */
2252 #if _FFR_BADRCPT_SHUTDOWN
2253 # define O_RCPTSHUTD    0xe1
2254         { "BadRcptShutdown",            O_RCPTSHUTD,    OI_SAFE },
2255 # define O_RCPTSHUTDG   0xe2
2256         { "BadRcptShutdownGood",        O_RCPTSHUTDG,   OI_SAFE },
2257 #endif /* _FFR_BADRCPT_SHUTDOWN */
2258
2259         { NULL,                         '\0',           OI_NONE }
2260 };
2261
2262 # define CANONIFY(val)
2263
2264 # define SET_OPT_DEFAULT(opt, val)      opt = val
2265
2266 /* set a string option by expanding the value and assigning it */
2267 /* WARNING this belongs ONLY into a case statement! */
2268 #define SET_STRING_EXP(str)     \
2269                 expand(val, exbuf, sizeof(exbuf), e);   \
2270                 newval = sm_pstrdup_x(exbuf);           \
2271                 if (str != NULL)        \
2272                         sm_free(str);   \
2273                 CANONIFY(newval);       \
2274                 str = newval;           \
2275                 break
2276
2277 #define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name
2278
2279 void
2280 setoption(opt, val, safe, sticky, e)
2281         int opt;
2282         char *val;
2283         bool safe;
2284         bool sticky;
2285         register ENVELOPE *e;
2286 {
2287         register char *p;
2288         register struct optioninfo *o;
2289         char *subopt;
2290         int mid;
2291         bool can_setuid = RunAsUid == 0;
2292         auto char *ep;
2293         char buf[50];
2294         extern bool Warn_Q_option;
2295 #if _FFR_ALLOW_SASLINFO
2296         extern unsigned int SubmitMode;
2297 #endif /* _FFR_ALLOW_SASLINFO */
2298 #if STARTTLS || SM_CONF_SHM
2299         char *newval;
2300         char exbuf[MAXLINE];
2301 #endif /* STARTTLS || SM_CONF_SHM */
2302
2303         errno = 0;
2304         if (opt == ' ')
2305         {
2306                 /* full word options */
2307                 struct optioninfo *sel;
2308
2309                 p = strchr(val, '=');
2310                 if (p == NULL)
2311                         p = &val[strlen(val)];
2312                 while (*--p == ' ')
2313                         continue;
2314                 while (*++p == ' ')
2315                         *p = '\0';
2316                 if (p == val)
2317                 {
2318                         syserr("readcf: null option name");
2319                         return;
2320                 }
2321                 if (*p == '=')
2322                         *p++ = '\0';
2323                 while (*p == ' ')
2324                         p++;
2325                 subopt = strchr(val, '.');
2326                 if (subopt != NULL)
2327                         *subopt++ = '\0';
2328                 sel = NULL;
2329                 for (o = OptionTab; o->o_name != NULL; o++)
2330                 {
2331                         if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
2332                                 continue;
2333                         if (strlen(o->o_name) == strlen(val))
2334                         {
2335                                 /* completely specified -- this must be it */
2336                                 sel = NULL;
2337                                 break;
2338                         }
2339                         if (sel != NULL)
2340                                 break;
2341                         sel = o;
2342                 }
2343                 if (sel != NULL && o->o_name == NULL)
2344                         o = sel;
2345                 else if (o->o_name == NULL)
2346                 {
2347                         syserr("readcf: unknown option name %s", val);
2348                         return;
2349                 }
2350                 else if (sel != NULL)
2351                 {
2352                         syserr("readcf: ambiguous option name %s (matches %s and %s)",
2353                                 val, sel->o_name, o->o_name);
2354                         return;
2355                 }
2356                 if (strlen(val) != strlen(o->o_name))
2357                 {
2358                         int oldVerbose = Verbose;
2359
2360                         Verbose = 1;
2361                         message("Option %s used as abbreviation for %s",
2362                                 val, o->o_name);
2363                         Verbose = oldVerbose;
2364                 }
2365                 opt = o->o_code;
2366                 val = p;
2367         }
2368         else
2369         {
2370                 for (o = OptionTab; o->o_name != NULL; o++)
2371                 {
2372                         if (o->o_code == opt)
2373                                 break;
2374                 }
2375                 if (o->o_name == NULL)
2376                 {
2377                         syserr("readcf: unknown option name 0x%x", opt & 0xff);
2378                         return;
2379                 }
2380                 subopt = NULL;
2381         }
2382
2383         if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
2384         {
2385                 if (tTd(37, 1))
2386                         sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
2387                                    OPTNAME, subopt);
2388                 subopt = NULL;
2389         }
2390
2391         if (tTd(37, 1))
2392         {
2393                 sm_dprintf(isascii(opt) && isprint(opt) ?
2394                            "setoption %s (%c)%s%s=" :
2395                            "setoption %s (0x%x)%s%s=",
2396                            OPTNAME, opt, subopt == NULL ? "" : ".",
2397                            subopt == NULL ? "" : subopt);
2398                 xputs(sm_debug_file(), val);
2399         }
2400
2401         /*
2402         **  See if this option is preset for us.
2403         */
2404
2405         if (!sticky && bitnset(opt, StickyOpt))
2406         {
2407                 if (tTd(37, 1))
2408                         sm_dprintf(" (ignored)\n");
2409                 return;
2410         }
2411
2412         /*
2413         **  Check to see if this option can be specified by this user.
2414         */
2415
2416         if (!safe && RealUid == 0)
2417                 safe = true;
2418         if (!safe && !bitset(OI_SAFE, o->o_flags))
2419         {
2420                 if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
2421                 {
2422                         int dp;
2423
2424                         if (tTd(37, 1))
2425                                 sm_dprintf(" (unsafe)");
2426                         dp = drop_privileges(true);
2427                         setstat(dp);
2428                 }
2429         }
2430         if (tTd(37, 1))
2431                 sm_dprintf("\n");
2432
2433         switch (opt & 0xff)
2434         {
2435           case '7':             /* force seven-bit input */
2436                 SevenBitInput = atobool(val);
2437                 break;
2438
2439           case '8':             /* handling of 8-bit input */
2440 #if MIME8TO7
2441                 switch (*val)
2442                 {
2443                   case 'p':             /* pass 8 bit, convert MIME */
2444                         MimeMode = MM_CVTMIME|MM_PASS8BIT;
2445                         break;
2446
2447                   case 'm':             /* convert 8-bit, convert MIME */
2448                         MimeMode = MM_CVTMIME|MM_MIME8BIT;
2449                         break;
2450
2451                   case 's':             /* strict adherence */
2452                         MimeMode = MM_CVTMIME;
2453                         break;
2454
2455 # if 0
2456                   case 'r':             /* reject 8-bit, don't convert MIME */
2457                         MimeMode = 0;
2458                         break;
2459
2460                   case 'j':             /* "just send 8" */
2461                         MimeMode = MM_PASS8BIT;
2462                         break;
2463
2464                   case 'a':             /* encode 8 bit if available */
2465                         MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
2466                         break;
2467
2468                   case 'c':             /* convert 8 bit to MIME, never 7 bit */
2469                         MimeMode = MM_MIME8BIT;
2470                         break;
2471 # endif /* 0 */
2472
2473                   default:
2474                         syserr("Unknown 8-bit mode %c", *val);
2475                         finis(false, true, EX_USAGE);
2476                 }
2477 #else /* MIME8TO7 */
2478                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2479                                      "Warning: Option: %s requires MIME8TO7 support\n",
2480                                      OPTNAME);
2481 #endif /* MIME8TO7 */
2482                 break;
2483
2484           case 'A':             /* set default alias file */
2485                 if (val[0] == '\0')
2486                 {
2487                         char *al;
2488
2489                         SET_OPT_DEFAULT(al, "aliases");
2490                         setalias(al);
2491                 }
2492                 else
2493                         setalias(val);
2494                 break;
2495
2496           case 'a':             /* look N minutes for "@:@" in alias file */
2497                 if (val[0] == '\0')
2498                         SafeAlias = 5 MINUTES;
2499                 else
2500                         SafeAlias = convtime(val, 'm');
2501                 break;
2502
2503           case 'B':             /* substitution for blank character */
2504                 SpaceSub = val[0];
2505                 if (SpaceSub == '\0')
2506                         SpaceSub = ' ';
2507                 break;
2508
2509           case 'b':             /* min blocks free on queue fs/max msg size */
2510                 p = strchr(val, '/');
2511                 if (p != NULL)
2512                 {
2513                         *p++ = '\0';
2514                         MaxMessageSize = atol(p);
2515                 }
2516                 MinBlocksFree = atol(val);
2517                 break;
2518
2519           case 'c':             /* don't connect to "expensive" mailers */
2520                 NoConnect = atobool(val);
2521                 break;
2522
2523           case 'C':             /* checkpoint every N addresses */
2524                 if (safe || CheckpointInterval > atoi(val))
2525                         CheckpointInterval = atoi(val);
2526                 break;
2527
2528           case 'd':             /* delivery mode */
2529                 switch (*val)
2530                 {
2531                   case '\0':
2532                         set_delivery_mode(SM_DELIVER, e);
2533                         break;
2534
2535                   case SM_QUEUE:        /* queue only */
2536                   case SM_DEFER:        /* queue only and defer map lookups */
2537                   case SM_DELIVER:      /* do everything */
2538                   case SM_FORK:         /* fork after verification */
2539 #if _FFR_DM_ONE
2540                 /* deliver first TA in background, then queue */
2541                   case SM_DM_ONE:
2542 #endif /* _FFR_DM_ONE */
2543                         set_delivery_mode(*val, e);
2544                         break;
2545
2546                   default:
2547                         syserr("Unknown delivery mode %c", *val);
2548                         finis(false, true, EX_USAGE);
2549                 }
2550                 break;
2551
2552           case 'E':             /* error message header/header file */
2553                 if (*val != '\0')
2554                         ErrMsgFile = newstr(val);
2555                 break;
2556
2557           case 'e':             /* set error processing mode */
2558                 switch (*val)
2559                 {
2560                   case EM_QUIET:        /* be silent about it */
2561                   case EM_MAIL:         /* mail back */
2562                   case EM_BERKNET:      /* do berknet error processing */
2563                   case EM_WRITE:        /* write back (or mail) */
2564                   case EM_PRINT:        /* print errors normally (default) */
2565                         e->e_errormode = *val;
2566                         break;
2567                 }
2568                 break;
2569
2570           case 'F':             /* file mode */
2571                 FileMode = atooct(val) & 0777;
2572                 break;
2573
2574           case 'f':             /* save Unix-style From lines on front */
2575                 SaveFrom = atobool(val);
2576                 break;
2577
2578           case 'G':             /* match recipients against GECOS field */
2579                 MatchGecos = atobool(val);
2580                 break;
2581
2582           case 'g':             /* default gid */
2583   g_opt:
2584                 if (isascii(*val) && isdigit(*val))
2585                         DefGid = atoi(val);
2586                 else
2587                 {
2588                         register struct group *gr;
2589
2590                         DefGid = -1;
2591                         gr = getgrnam(val);
2592                         if (gr == NULL)
2593                                 syserr("readcf: option %c: unknown group %s",
2594                                         opt, val);
2595                         else
2596                                 DefGid = gr->gr_gid;
2597                 }
2598                 break;
2599
2600           case 'H':             /* help file */
2601                 if (val[0] == '\0')
2602                 {
2603                         SET_OPT_DEFAULT(HelpFile, "helpfile");
2604                 }
2605                 else
2606                 {
2607                         CANONIFY(val);
2608                         HelpFile = newstr(val);
2609                 }
2610                 break;
2611
2612           case 'h':             /* maximum hop count */
2613                 MaxHopCount = atoi(val);
2614                 break;
2615
2616           case 'I':             /* use internet domain name server */
2617 #if NAMED_BIND
2618                 for (p = val; *p != 0; )
2619                 {
2620                         bool clearmode;
2621                         char *q;
2622                         struct resolverflags *rfp;
2623
2624                         while (*p == ' ')
2625                                 p++;
2626                         if (*p == '\0')
2627                                 break;
2628                         clearmode = false;
2629                         if (*p == '-')
2630                                 clearmode = true;
2631                         else if (*p != '+')
2632                                 p--;
2633                         p++;
2634                         q = p;
2635                         while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2636                                 p++;
2637                         if (*p != '\0')
2638                                 *p++ = '\0';
2639                         if (sm_strcasecmp(q, "HasWildcardMX") == 0)
2640                         {
2641                                 HasWildcardMX = !clearmode;
2642                                 continue;
2643                         }
2644                         if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
2645                         {
2646                                 WorkAroundBrokenAAAA = !clearmode;
2647                                 continue;
2648                         }
2649                         for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2650                         {
2651                                 if (sm_strcasecmp(q, rfp->rf_name) == 0)
2652                                         break;
2653                         }
2654                         if (rfp->rf_name == NULL)
2655                                 syserr("readcf: I option value %s unrecognized", q);
2656                         else if (clearmode)
2657                                 _res.options &= ~rfp->rf_bits;
2658                         else
2659                                 _res.options |= rfp->rf_bits;
2660                 }
2661                 if (tTd(8, 2))
2662                         sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
2663                                    (unsigned int) _res.options, HasWildcardMX);
2664 #else /* NAMED_BIND */
2665                 usrerr("name server (I option) specified but BIND not compiled in");
2666 #endif /* NAMED_BIND */
2667                 break;
2668
2669           case 'i':             /* ignore dot lines in message */
2670                 IgnrDot = atobool(val);
2671                 break;
2672
2673           case 'j':             /* send errors in MIME (RFC 1341) format */
2674                 SendMIMEErrors = atobool(val);
2675                 break;
2676
2677           case 'J':             /* .forward search path */
2678                 CANONIFY(val);
2679                 ForwardPath = newstr(val);
2680                 break;
2681
2682           case 'k':             /* connection cache size */
2683                 MaxMciCache = atoi(val);
2684                 if (MaxMciCache < 0)
2685                         MaxMciCache = 0;
2686                 break;
2687
2688           case 'K':             /* connection cache timeout */
2689                 MciCacheTimeout = convtime(val, 'm');
2690                 break;
2691
2692           case 'l':             /* use Errors-To: header */
2693                 UseErrorsTo = atobool(val);
2694                 break;
2695
2696           case 'L':             /* log level */
2697                 if (safe || LogLevel < atoi(val))
2698                         LogLevel = atoi(val);
2699                 break;
2700
2701           case 'M':             /* define macro */
2702                 sticky = false;
2703                 mid = macid_parse(val, &ep);
2704                 if (mid == 0)
2705                         break;
2706                 p = newstr(ep);
2707                 if (!safe)
2708                         cleanstrcpy(p, p, strlen(p) + 1);
2709                 macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
2710                 break;
2711
2712           case 'm':             /* send to me too */
2713                 MeToo = atobool(val);
2714                 break;
2715
2716           case 'n':             /* validate RHS in newaliases */
2717                 CheckAliases = atobool(val);
2718                 break;
2719
2720             /* 'N' available -- was "net name" */
2721
2722           case 'O':             /* daemon options */
2723                 if (!setdaemonoptions(val))
2724                         syserr("too many daemons defined (%d max)", MAXDAEMONS);
2725                 break;
2726
2727           case 'o':             /* assume old style headers */
2728                 if (atobool(val))
2729                         CurEnv->e_flags |= EF_OLDSTYLE;
2730                 else
2731                         CurEnv->e_flags &= ~EF_OLDSTYLE;
2732                 break;
2733
2734           case 'p':             /* select privacy level */
2735                 p = val;
2736                 for (;;)
2737                 {
2738                         register struct prival *pv;
2739                         extern struct prival PrivacyValues[];
2740
2741                         while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2742                                 p++;
2743                         if (*p == '\0')
2744                                 break;
2745                         val = p;
2746                         while (isascii(*p) && isalnum(*p))
2747                                 p++;
2748                         if (*p != '\0')
2749                                 *p++ = '\0';
2750
2751                         for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2752                         {
2753                                 if (sm_strcasecmp(val, pv->pv_name) == 0)
2754                                         break;
2755                         }
2756                         if (pv->pv_name == NULL)
2757                                 syserr("readcf: Op line: %s unrecognized", val);
2758                         else
2759                                 PrivacyFlags |= pv->pv_flag;
2760                 }
2761                 sticky = false;
2762                 break;
2763
2764           case 'P':             /* postmaster copy address for returned mail */
2765                 PostMasterCopy = newstr(val);
2766                 break;
2767
2768           case 'q':             /* slope of queue only function */
2769                 QueueFactor = atoi(val);
2770                 break;
2771
2772           case 'Q':             /* queue directory */
2773                 if (val[0] == '\0')
2774                 {
2775                         QueueDir = "mqueue";
2776                 }
2777                 else
2778                 {
2779                         QueueDir = newstr(val);
2780                 }
2781                 if (RealUid != 0 && !safe)
2782                         Warn_Q_option = true;
2783                 break;
2784
2785           case 'R':             /* don't prune routes */
2786                 DontPruneRoutes = atobool(val);
2787                 break;
2788
2789           case 'r':             /* read timeout */
2790                 if (subopt == NULL)
2791                         inittimeouts(val, sticky);
2792                 else
2793                         settimeout(subopt, val, sticky);
2794                 break;
2795
2796           case 'S':             /* status file */
2797                 if (val[0] == '\0')
2798                 {
2799                         SET_OPT_DEFAULT(StatFile, "statistics");
2800                 }
2801                 else
2802                 {
2803                         CANONIFY(val);
2804                         StatFile = newstr(val);
2805                 }
2806                 break;
2807
2808           case 's':             /* be super safe, even if expensive */
2809                 if (tolower(*val) == 'i')
2810                         SuperSafe = SAFE_INTERACTIVE;
2811                 else if (tolower(*val) == 'p')
2812 #if MILTER
2813                         SuperSafe = SAFE_REALLY_POSTMILTER;
2814 #else /* MILTER */
2815                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2816                                 "Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
2817 #endif /* MILTER */
2818                 else
2819                         SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
2820                 break;
2821
2822           case 'T':             /* queue timeout */
2823                 p = strchr(val, '/');
2824                 if (p != NULL)
2825                 {
2826                         *p++ = '\0';
2827                         settimeout("queuewarn", p, sticky);
2828                 }
2829                 settimeout("queuereturn", val, sticky);
2830                 break;
2831
2832           case 't':             /* time zone name */
2833                 TimeZoneSpec = newstr(val);
2834                 break;
2835
2836           case 'U':             /* location of user database */
2837                 UdbSpec = newstr(val);
2838                 break;
2839
2840           case 'u':             /* set default uid */
2841                 for (p = val; *p != '\0'; p++)
2842                 {
2843 # if _FFR_DOTTED_USERNAMES
2844                         if (*p == '/' || *p == ':')
2845 # else /* _FFR_DOTTED_USERNAMES */
2846                         if (*p == '.' || *p == '/' || *p == ':')
2847 # endif /* _FFR_DOTTED_USERNAMES */
2848                         {
2849                                 *p++ = '\0';
2850                                 break;
2851                         }
2852                 }
2853                 if (isascii(*val) && isdigit(*val))
2854                 {
2855                         DefUid = atoi(val);
2856                         setdefuser();
2857                 }
2858                 else
2859                 {
2860                         register struct passwd *pw;
2861
2862                         DefUid = -1;
2863                         pw = sm_getpwnam(val);
2864                         if (pw == NULL)
2865                         {
2866                                 syserr("readcf: option u: unknown user %s", val);
2867                                 break;
2868                         }
2869                         else
2870                         {
2871                                 DefUid = pw->pw_uid;
2872                                 DefGid = pw->pw_gid;
2873                                 DefUser = newstr(pw->pw_name);
2874                         }
2875                 }
2876
2877 # ifdef UID_MAX
2878                 if (DefUid > UID_MAX)
2879                 {
2880                         syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2881                                 (long)DefUid, (long)UID_MAX);
2882                         break;
2883                 }
2884 # endif /* UID_MAX */
2885
2886                 /* handle the group if it is there */
2887                 if (*p == '\0')
2888                         break;
2889                 val = p;
2890                 goto g_opt;
2891
2892           case 'V':             /* fallback MX host */
2893                 if (val[0] != '\0')
2894                         FallbackMX = newstr(val);
2895                 break;
2896
2897           case 'v':             /* run in verbose mode */
2898                 Verbose = atobool(val) ? 1 : 0;
2899                 break;
2900
2901           case 'w':             /* if we are best MX, try host directly */
2902                 TryNullMXList = atobool(val);
2903                 break;
2904
2905             /* 'W' available -- was wizard password */
2906
2907           case 'x':             /* load avg at which to auto-queue msgs */
2908                 QueueLA = atoi(val);
2909                 break;
2910
2911           case 'X':     /* load avg at which to auto-reject connections */
2912                 RefuseLA = atoi(val);
2913                 break;
2914
2915           case O_DELAY_LA:      /* load avg at which to delay connections */
2916                 DelayLA = atoi(val);
2917                 break;
2918
2919           case 'y':             /* work recipient factor */
2920                 WkRecipFact = atoi(val);
2921                 break;
2922
2923           case 'Y':             /* fork jobs during queue runs */
2924                 ForkQueueRuns = atobool(val);
2925                 break;
2926
2927           case 'z':             /* work message class factor */
2928                 WkClassFact = atoi(val);
2929                 break;
2930
2931           case 'Z':             /* work time factor */
2932                 WkTimeFact = atoi(val);
2933                 break;
2934
2935
2936 #if _FFR_QUEUE_GROUP_SORTORDER
2937         /* coordinate this with makequeue() */
2938 #endif /* _FFR_QUEUE_GROUP_SORTORDER */
2939           case O_QUEUESORTORD:  /* queue sorting order */
2940                 switch (*val)
2941                 {
2942                   case 'f':     /* File Name */
2943                   case 'F':
2944                         QueueSortOrder = QSO_BYFILENAME;
2945                         break;
2946
2947                   case 'h':     /* Host first */
2948                   case 'H':
2949                         QueueSortOrder = QSO_BYHOST;
2950                         break;
2951
2952                   case 'm':     /* Modification time */
2953                   case 'M':
2954                         QueueSortOrder = QSO_BYMODTIME;
2955                         break;
2956
2957                   case 'p':     /* Priority order */
2958                   case 'P':
2959                         QueueSortOrder = QSO_BYPRIORITY;
2960                         break;
2961
2962                   case 't':     /* Submission time */
2963                   case 'T':
2964                         QueueSortOrder = QSO_BYTIME;
2965                         break;
2966
2967                   case 'r':     /* Random */
2968                   case 'R':
2969                         QueueSortOrder = QSO_RANDOM;
2970                         break;
2971
2972 #if _FFR_RHS
2973                   case 's':     /* Shuffled host name */
2974                   case 'S':
2975                         QueueSortOrder = QSO_BYSHUFFLE;
2976                         break;
2977 #endif /* _FFR_RHS */
2978
2979                   case 'n':     /* none */
2980                   case 'N':
2981                         QueueSortOrder = QSO_NONE;
2982                         break;
2983
2984                   default:
2985                         syserr("Invalid queue sort order \"%s\"", val);
2986                 }
2987                 break;
2988
2989           case O_HOSTSFILE:     /* pathname of /etc/hosts file */
2990                 CANONIFY(val);
2991                 HostsFile = newstr(val);
2992                 break;
2993
2994           case O_MQA:           /* minimum queue age between deliveries */
2995                 MinQueueAge = convtime(val, 'm');
2996                 break;
2997
2998           case O_DEFCHARSET:    /* default character set for mimefying */
2999                 DefaultCharSet = newstr(denlstring(val, true, true));
3000                 break;
3001
3002           case O_SSFILE:        /* service switch file */
3003                 CANONIFY(val);
3004                 ServiceSwitchFile = newstr(val);
3005                 break;
3006
3007           case O_DIALDELAY:     /* delay for dial-on-demand operation */
3008                 DialDelay = convtime(val, 's');
3009                 break;
3010
3011           case O_NORCPTACTION:  /* what to do if no recipient */
3012                 if (sm_strcasecmp(val, "none") == 0)
3013                         NoRecipientAction = NRA_NO_ACTION;
3014                 else if (sm_strcasecmp(val, "add-to") == 0)
3015                         NoRecipientAction = NRA_ADD_TO;
3016                 else if (sm_strcasecmp(val, "add-apparently-to") == 0)
3017                         NoRecipientAction = NRA_ADD_APPARENTLY_TO;
3018                 else if (sm_strcasecmp(val, "add-bcc") == 0)
3019                         NoRecipientAction = NRA_ADD_BCC;
3020                 else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
3021                         NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
3022                 else
3023                         syserr("Invalid NoRecipientAction: %s", val);
3024                 break;
3025
3026           case O_SAFEFILEENV:   /* chroot() environ for writing to files */
3027                 if (*val == '\0')
3028                         break;
3029
3030                 /* strip trailing slashes */
3031                 p = val + strlen(val) - 1;
3032                 while (p >= val && *p == '/')
3033                         *p-- = '\0';
3034
3035                 if (*val == '\0')
3036                         break;
3037
3038                 SafeFileEnv = newstr(val);
3039                 break;
3040
3041           case O_MAXMSGSIZE:    /* maximum message size */
3042                 MaxMessageSize = atol(val);
3043                 break;
3044
3045           case O_COLONOKINADDR: /* old style handling of colon addresses */
3046                 ColonOkInAddr = atobool(val);
3047                 break;
3048
3049           case O_MAXQUEUERUN:   /* max # of jobs in a single queue run */
3050                 MaxQueueRun = atoi(val);
3051                 break;
3052
3053           case O_MAXCHILDREN:   /* max # of children of daemon */
3054                 MaxChildren = atoi(val);
3055                 break;
3056
3057           case O_MAXQUEUECHILDREN: /* max # of children of daemon */
3058                 MaxQueueChildren = atoi(val);
3059                 break;
3060
3061           case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
3062                 MaxRunnersPerQueue = atoi(val);
3063                 break;
3064
3065           case O_NICEQUEUERUN:          /* nice queue runs */
3066 #if !HASNICE
3067                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3068                                      "Warning: NiceQueueRun set on system that doesn't support nice()\n");
3069 #endif /* !HASNICE */
3070
3071                 /* XXX do we want to check the range? > 0 ? */
3072                 NiceQueueRun = atoi(val);
3073                 break;
3074
3075           case O_SHMKEY:                /* shared memory key */
3076 #if SM_CONF_SHM
3077                 ShmKey = atol(val);
3078 #else /* SM_CONF_SHM */
3079                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3080                                      "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3081                                      OPTNAME);
3082 #endif /* SM_CONF_SHM */
3083                 break;
3084
3085           case O_SHMKEYFILE:            /* shared memory key file */
3086 #if SM_CONF_SHM
3087                 SET_STRING_EXP(ShmKeyFile);
3088 #else /* SM_CONF_SHM */
3089                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3090                                      "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3091                                      OPTNAME);
3092                 break;
3093 #endif /* SM_CONF_SHM */
3094
3095 #if _FFR_MAX_FORWARD_ENTRIES
3096           case O_MAXFORWARD:    /* max # of forward entries */
3097                 MaxForwardEntries = atoi(val);
3098                 break;
3099 #endif /* _FFR_MAX_FORWARD_ENTRIES */
3100
3101           case O_KEEPCNAMES:    /* don't expand CNAME records */
3102                 DontExpandCnames = atobool(val);
3103                 break;
3104
3105           case O_MUSTQUOTE:     /* must quote these characters in phrases */
3106                 (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof(buf));
3107                 if (strlen(val) < sizeof(buf) - 10)
3108                         (void) sm_strlcat(buf, val, sizeof(buf));
3109                 else
3110                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3111                                              "Warning: MustQuoteChars too long, ignored.\n");
3112                 MustQuoteChars = newstr(buf);
3113                 break;
3114
3115           case O_SMTPGREETING:  /* SMTP greeting message (old $e macro) */
3116                 SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
3117                 break;
3118
3119           case O_UNIXFROM:      /* UNIX From_ line (old $l macro) */
3120                 UnixFromLine = newstr(munchstring(val, NULL, '\0'));
3121                 break;
3122
3123           case O_OPCHARS:       /* operator characters (old $o macro) */
3124                 if (OperatorChars != NULL)
3125                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3126                                              "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
3127                 OperatorChars = newstr(munchstring(val, NULL, '\0'));
3128                 break;
3129
3130           case O_DONTINITGRPS:  /* don't call initgroups(3) */
3131                 DontInitGroups = atobool(val);
3132                 break;
3133
3134           case O_SLFH:          /* make sure from fits on one line */
3135                 SingleLineFromHeader = atobool(val);
3136                 break;
3137
3138           case O_ABH:           /* allow HELO commands with syntax errors */
3139                 AllowBogusHELO = atobool(val);
3140                 break;
3141
3142           case O_CONNTHROT:     /* connection rate throttle */
3143                 ConnRateThrottle = atoi(val);
3144                 break;
3145
3146           case O_UGW:           /* group writable files are unsafe */
3147                 if (!atobool(val))
3148                 {
3149                         setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
3150                                 DontBlameSendmail);
3151                         setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
3152                                 DontBlameSendmail);
3153                 }
3154                 break;
3155
3156           case O_DBLBOUNCE:     /* address to which to send double bounces */
3157                 DoubleBounceAddr = newstr(val);
3158                 break;
3159
3160           case O_HSDIR:         /* persistent host status directory */
3161                 if (val[0] != '\0')
3162                 {
3163                         CANONIFY(val);
3164                         HostStatDir = newstr(val);
3165                 }
3166                 break;
3167
3168           case O_SINGTHREAD:    /* single thread deliveries (requires hsdir) */
3169                 SingleThreadDelivery = atobool(val);
3170                 break;
3171
3172           case O_RUNASUSER:     /* run bulk of code as this user */
3173                 for (p = val; *p != '\0'; p++)
3174                 {
3175 # if _FFR_DOTTED_USERNAMES
3176                         if (*p == '/' || *p == ':')
3177 # else /* _FFR_DOTTED_USERNAMES */
3178                         if (*p == '.' || *p == '/' || *p == ':')
3179 # endif /* _FFR_DOTTED_USERNAMES */
3180                         {
3181                                 *p++ = '\0';
3182                                 break;
3183                         }
3184                 }
3185                 if (isascii(*val) && isdigit(*val))
3186                 {
3187                         if (can_setuid)
3188                                 RunAsUid = atoi(val);
3189                 }
3190                 else
3191                 {
3192                         register struct passwd *pw;
3193
3194                         pw = sm_getpwnam(val);
3195                         if (pw == NULL)
3196                         {
3197                                 syserr("readcf: option RunAsUser: unknown user %s", val);
3198                                 break;
3199                         }
3200                         else if (can_setuid)
3201                         {
3202                                 if (*p == '\0')
3203                                         RunAsUserName = newstr(val);
3204                                 RunAsUid = pw->pw_uid;
3205                                 RunAsGid = pw->pw_gid;
3206                         }
3207                         else if (EffGid == pw->pw_gid)
3208                                 RunAsGid = pw->pw_gid;
3209                         else if (UseMSP && *p == '\0')
3210                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3211                                                      "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3212                                                      (int) EffGid,
3213                                                      (int) pw->pw_gid);
3214                 }
3215 # ifdef UID_MAX
3216                 if (RunAsUid > UID_MAX)
3217                 {
3218                         syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3219                                 (long) RunAsUid, (long) UID_MAX);
3220                         break;
3221                 }
3222 # endif /* UID_MAX */
3223                 if (*p != '\0')
3224                 {
3225                         if (isascii(*p) && isdigit(*p))
3226                         {
3227                                 gid_t runasgid;
3228
3229                                 runasgid = (gid_t) atoi(p);
3230                                 if (can_setuid || EffGid == runasgid)
3231                                         RunAsGid = runasgid;
3232                                 else if (UseMSP)
3233                                         (void) sm_io_fprintf(smioout,
3234                                                              SM_TIME_DEFAULT,
3235                                                              "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3236                                                              (int) EffGid,
3237                                                              (int) runasgid);
3238                         }
3239                         else
3240                         {
3241                                 register struct group *gr;
3242
3243                                 gr = getgrnam(p);
3244                                 if (gr == NULL)
3245                                         syserr("readcf: option RunAsUser: unknown group %s",
3246                                                 p);
3247                                 else if (can_setuid || EffGid == gr->gr_gid)
3248                                         RunAsGid = gr->gr_gid;
3249                                 else if (UseMSP)
3250                                         (void) sm_io_fprintf(smioout,
3251                                                              SM_TIME_DEFAULT,
3252                                                              "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3253                                                              (int) EffGid,
3254                                                              (int) gr->gr_gid);
3255                         }
3256                 }
3257                 if (tTd(47, 5))
3258                         sm_dprintf("readcf: RunAsUser = %d:%d\n",
3259                                    (int) RunAsUid, (int) RunAsGid);
3260                 break;
3261
3262           case O_DSN_RRT:
3263                 RrtImpliesDsn = atobool(val);
3264                 break;
3265
3266           case O_PIDFILE:
3267                 PSTRSET(PidFile, val);
3268                 break;
3269
3270           case O_DONTBLAMESENDMAIL:
3271                 p = val;
3272                 for (;;)
3273                 {
3274                         register struct dbsval *dbs;
3275                         extern struct dbsval DontBlameSendmailValues[];
3276
3277                         while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3278                                 p++;
3279                         if (*p == '\0')
3280                                 break;
3281                         val = p;
3282                         while (isascii(*p) && isalnum(*p))
3283                                 p++;
3284                         if (*p != '\0')
3285                                 *p++ = '\0';
3286
3287                         for (dbs = DontBlameSendmailValues;
3288                              dbs->dbs_name != NULL; dbs++)
3289                         {
3290                                 if (sm_strcasecmp(val, dbs->dbs_name) == 0)
3291                                         break;
3292                         }
3293                         if (dbs->dbs_name == NULL)
3294                                 syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
3295                         else if (dbs->dbs_flag == DBS_SAFE)
3296                                 clrbitmap(DontBlameSendmail);
3297                         else
3298                                 setbitn(dbs->dbs_flag, DontBlameSendmail);
3299                 }
3300                 sticky = false;
3301                 break;
3302
3303           case O_DPI:
3304                 if (sm_strcasecmp(val, "loopback") == 0)
3305                         DontProbeInterfaces = DPI_SKIPLOOPBACK;
3306                 else if (atobool(val))
3307                         DontProbeInterfaces = DPI_PROBENONE;
3308                 else
3309                         DontProbeInterfaces = DPI_PROBEALL;
3310                 break;
3311
3312           case O_MAXRCPT:
3313                 MaxRcptPerMsg = atoi(val);
3314                 break;
3315
3316           case O_RCPTTHROT:
3317                 BadRcptThrottle = atoi(val);
3318                 break;
3319
3320           case O_DEADLETTER:
3321                 CANONIFY(val);
3322                 PSTRSET(DeadLetterDrop, val);
3323                 break;
3324
3325 #if _FFR_DONTLOCKFILESFORREAD_OPTION
3326           case O_DONTLOCK:
3327                 DontLockReadFiles = atobool(val);
3328                 break;
3329 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
3330
3331           case O_MAXALIASRCSN:
3332                 MaxAliasRecursion = atoi(val);
3333                 break;
3334
3335           case O_CNCTONLYTO:
3336                 /* XXX should probably use gethostbyname */
3337 #if NETINET || NETINET6
3338                 ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
3339 # if NETINET6
3340                 if (anynet_pton(AF_INET6, val,
3341                                 &ConnectOnlyTo.sin6.sin6_addr) != 1)
3342                         ConnectOnlyTo.sa.sa_family = AF_INET6;
3343                 else
3344 # endif /* NETINET6 */
3345 # if NETINET
3346                 {
3347                         ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
3348                         if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
3349                                 ConnectOnlyTo.sa.sa_family = AF_INET;
3350                 }
3351
3352 # endif /* NETINET */
3353                 if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
3354                 {
3355                         syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
3356                                val);
3357                         break;
3358                 }
3359 #endif /* NETINET || NETINET6 */
3360                 break;
3361
3362           case O_TRUSTUSER:
3363 # if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
3364                 if (!UseMSP)
3365                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3366                                              "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
3367 # endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
3368                 if (isascii(*val) && isdigit(*val))
3369                         TrustedUid = atoi(val);
3370                 else
3371                 {
3372                         register struct passwd *pw;
3373
3374                         TrustedUid = 0;
3375                         pw = sm_getpwnam(val);
3376                         if (pw == NULL)
3377                         {
3378                                 syserr("readcf: option TrustedUser: unknown user %s", val);
3379                                 break;
3380                         }
3381                         else
3382                                 TrustedUid = pw->pw_uid;
3383                 }
3384
3385 # ifdef UID_MAX
3386                 if (TrustedUid > UID_MAX)
3387                 {
3388                         syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
3389                                 (long) TrustedUid, (long) UID_MAX);
3390                         TrustedUid = 0;
3391                 }
3392 # endif /* UID_MAX */
3393                 break;
3394
3395           case O_MAXMIMEHDRLEN:
3396                 p = strchr(val, '/');
3397                 if (p != NULL)
3398                         *p++ = '\0';
3399                 MaxMimeHeaderLength = atoi(val);
3400                 if (p != NULL && *p != '\0')
3401                         MaxMimeFieldLength = atoi(p);
3402                 else
3403                         MaxMimeFieldLength = MaxMimeHeaderLength / 2;
3404
3405                 if (MaxMimeHeaderLength <= 0)
3406                         MaxMimeHeaderLength = 0;
3407                 else if (MaxMimeHeaderLength < 128)
3408                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3409                                              "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
3410
3411                 if (MaxMimeFieldLength <= 0)
3412                         MaxMimeFieldLength = 0;
3413                 else if (MaxMimeFieldLength < 40)
3414                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3415                                              "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
3416
3417                 /*
3418                 **  Headers field values now include leading space, so let's
3419                 **  adjust the values to be "backward compatible".
3420                 */
3421
3422                 if (MaxMimeHeaderLength > 0)
3423                         MaxMimeHeaderLength++;
3424                 if (MaxMimeFieldLength > 0)
3425                         MaxMimeFieldLength++;
3426                 break;
3427
3428           case O_CONTROLSOCKET:
3429                 PSTRSET(ControlSocketName, val);
3430                 break;
3431
3432           case O_MAXHDRSLEN:
3433                 MaxHeadersLength = atoi(val);
3434
3435                 if (MaxHeadersLength > 0 &&
3436                     MaxHeadersLength < (MAXHDRSLEN / 2))
3437                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3438                                              "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
3439                                              (MAXHDRSLEN / 2));
3440                 break;
3441
3442           case O_PROCTITLEPREFIX:
3443                 PSTRSET(ProcTitlePrefix, val);
3444                 break;
3445
3446 #if SASL
3447           case O_SASLINFO:
3448 # if _FFR_ALLOW_SASLINFO
3449                 /*
3450                 **  Allow users to select their own authinfo file
3451                 **  under certain circumstances, otherwise just ignore
3452                 **  the option.  If the option isn't ignored, several
3453                 **  commands don't work very well, e.g., mailq.
3454                 **  However, this is not a "perfect" solution.
3455                 **  If mail is queued, the authentication info
3456                 **  will not be used in subsequent delivery attempts.
3457                 **  If we really want to support this, then it has
3458                 **  to be stored in the queue file.
3459                 */
3460                 if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
3461                     RunAsUid != RealUid)
3462                         break;
3463 # endif /* _FFR_ALLOW_SASLINFO */
3464                 PSTRSET(SASLInfo, val);
3465                 break;
3466
3467           case O_SASLMECH:
3468                 if (AuthMechanisms != NULL)
3469                         sm_free(AuthMechanisms); /* XXX */
3470                 if (*val != '\0')
3471                         AuthMechanisms = newstr(val);
3472                 else
3473                         AuthMechanisms = NULL;
3474                 break;
3475
3476           case O_SASLREALM:
3477                 if (AuthRealm != NULL)
3478                         sm_free(AuthRealm);
3479                 if (*val != '\0')
3480                         AuthRealm = newstr(val);
3481                 else
3482                         AuthRealm = NULL;
3483                 break;
3484
3485           case O_SASLOPTS:
3486                 while (val != NULL && *val != '\0')
3487                 {
3488                         switch (*val)
3489                         {
3490                           case 'A':
3491                                 SASLOpts |= SASL_AUTH_AUTH;
3492                                 break;
3493
3494                           case 'a':
3495                                 SASLOpts |= SASL_SEC_NOACTIVE;
3496                                 break;
3497
3498                           case 'c':
3499                                 SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
3500                                 break;
3501
3502                           case 'd':
3503                                 SASLOpts |= SASL_SEC_NODICTIONARY;
3504                                 break;
3505
3506                           case 'f':
3507                                 SASLOpts |= SASL_SEC_FORWARD_SECRECY;
3508                                 break;
3509
3510 #  if SASL >= 20101
3511                           case 'm':
3512                                 SASLOpts |= SASL_SEC_MUTUAL_AUTH;
3513                                 break;
3514 #  endif /* SASL >= 20101 */
3515
3516                           case 'p':
3517                                 SASLOpts |= SASL_SEC_NOPLAINTEXT;
3518                                 break;
3519
3520                           case 'y':
3521                                 SASLOpts |= SASL_SEC_NOANONYMOUS;
3522                                 break;
3523
3524                           case ' ':     /* ignore */
3525                           case '\t':    /* ignore */
3526                           case ',':     /* ignore */
3527                                 break;
3528
3529                           default:
3530                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3531                                                      "Warning: Option: %s unknown parameter '%c'\n",
3532                                                      OPTNAME,
3533                                                      (isascii(*val) &&
3534                                                         isprint(*val))
3535                                                         ? *val : '?');
3536                                 break;
3537                         }
3538                         ++val;
3539                         val = strpbrk(val, ", \t");
3540                         if (val != NULL)
3541                                 ++val;
3542                 }
3543                 break;
3544
3545           case O_SASLBITS:
3546                 MaxSLBits = atoi(val);
3547                 break;
3548
3549 #else /* SASL */
3550           case O_SASLINFO:
3551           case O_SASLMECH:
3552           case O_SASLREALM:
3553           case O_SASLOPTS:
3554           case O_SASLBITS:
3555                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3556                                      "Warning: Option: %s requires SASL support (-DSASL)\n",
3557                                      OPTNAME);
3558                 break;
3559 #endif /* SASL */
3560
3561 #if STARTTLS
3562           case O_SRVCERTFILE:
3563                 SET_STRING_EXP(SrvCertFile);
3564           case O_SRVKEYFILE:
3565                 SET_STRING_EXP(SrvKeyFile);
3566           case O_CLTCERTFILE:
3567                 SET_STRING_EXP(CltCertFile);
3568           case O_CLTKEYFILE:
3569                 SET_STRING_EXP(CltKeyFile);
3570           case O_CACERTFILE:
3571                 SET_STRING_EXP(CACertFile);
3572           case O_CACERTPATH:
3573                 SET_STRING_EXP(CACertPath);
3574           case O_DHPARAMS:
3575                 SET_STRING_EXP(DHParams);
3576 # if _FFR_TLS_1
3577           case O_DHPARAMS5:
3578                 SET_STRING_EXP(DHParams5);
3579           case O_CIPHERLIST:
3580                 SET_STRING_EXP(CipherList);
3581 # endif /* _FFR_TLS_1 */
3582           case O_CRLFILE:
3583 # if OPENSSL_VERSION_NUMBER > 0x00907000L
3584                 SET_STRING_EXP(CRLFile);
3585 # else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3586                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3587                                      "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
3588                                      OPTNAME);
3589                 break;
3590 # endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3591
3592 # if _FFR_CRLPATH
3593           case O_CRLPATH:
3594 #  if OPENSSL_VERSION_NUMBER > 0x00907000L
3595                 SET_STRING_EXP(CRLPath);
3596 #  else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3597                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3598                                      "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
3599                                      OPTNAME);
3600                 break;
3601 #  endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3602 # endif /* _FFR_CRLPATH */
3603
3604         /*
3605         **  XXX How about options per daemon/client instead of globally?
3606         **  This doesn't work well for some options, e.g., no server cert,
3607         **  but fine for others.
3608         **
3609         **  XXX Some people may want different certs per server.
3610         **
3611         **  See also srvfeatures()
3612         */
3613
3614           case O_TLS_SRV_OPTS:
3615                 while (val != NULL && *val != '\0')
3616                 {
3617                         switch (*val)
3618                         {
3619                           case 'V':
3620                                 TLS_Srv_Opts |= TLS_I_NO_VRFY;
3621                                 break;
3622 # if _FFR_TLS_1
3623                         /*
3624                         **  Server without a cert? That works only if
3625                         **  AnonDH is enabled as cipher, which is not in the
3626                         **  default list. Hence the CipherList option must
3627                         **  be available. Moreover: which clients support this
3628                         **  besides sendmail with this setting?
3629                         */
3630
3631                           case 'C':
3632                                 TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
3633                                 break;
3634 # endif /* _FFR_TLS_1 */
3635                           case ' ':     /* ignore */
3636                           case '\t':    /* ignore */
3637                           case ',':     /* ignore */
3638                                 break;
3639                           default:
3640                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3641                                                      "Warning: Option: %s unknown parameter '%c'\n",
3642                                                      OPTNAME,
3643                                                      (isascii(*val) &&
3644                                                         isprint(*val))
3645                                                         ? *val : '?');
3646                                 break;
3647                         }
3648                         ++val;
3649                         val = strpbrk(val, ", \t");
3650                         if (val != NULL)
3651                                 ++val;
3652                 }
3653                 break;
3654
3655           case O_RANDFILE:
3656                 PSTRSET(RandFile, val);
3657                 break;
3658
3659 #else /* STARTTLS */
3660           case O_SRVCERTFILE:
3661           case O_SRVKEYFILE:
3662           case O_CLTCERTFILE:
3663           case O_CLTKEYFILE:
3664           case O_CACERTFILE:
3665           case O_CACERTPATH:
3666           case O_DHPARAMS:
3667 # if _FFR_TLS_1
3668           case O_DHPARAMS5:
3669           case O_CIPHERLIST:
3670 # endif /* _FFR_TLS_1 */
3671           case O_CRLFILE:
3672 # if _FFR_CRLPATH
3673           case O_CRLPATH:
3674 # endif /* _FFR_CRLPATH */
3675           case O_RANDFILE:
3676                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3677                                      "Warning: Option: %s requires TLS support\n",
3678                                      OPTNAME);
3679                 break;
3680
3681 #endif /* STARTTLS */
3682
3683           case O_CLIENTPORT:
3684                 setclientoptions(val);
3685                 break;
3686
3687           case O_DF_BUFSIZE:
3688                 DataFileBufferSize = atoi(val);
3689                 break;
3690
3691           case O_XF_BUFSIZE:
3692                 XscriptFileBufferSize = atoi(val);
3693                 break;
3694
3695           case O_LDAPDEFAULTSPEC:
3696 #if LDAPMAP
3697                 ldapmap_set_defaults(val);
3698 #else /* LDAPMAP */
3699                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3700                                      "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
3701                                      OPTNAME);
3702 #endif /* LDAPMAP */
3703                 break;
3704
3705           case O_INPUTMILTER:
3706 #if MILTER
3707                 InputFilterList = newstr(val);
3708 #else /* MILTER */
3709                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3710                                      "Warning: Option: %s requires Milter support (-DMILTER)\n",
3711                                      OPTNAME);
3712 #endif /* MILTER */
3713                 break;
3714
3715           case O_MILTER:
3716 #if MILTER
3717                 milter_set_option(subopt, val, sticky);
3718 #else /* MILTER */
3719                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3720                                      "Warning: Option: %s requires Milter support (-DMILTER)\n",
3721                                      OPTNAME);
3722 #endif /* MILTER */
3723                 break;
3724
3725           case O_QUEUE_FILE_MODE:       /* queue file mode */
3726                 QueueFileMode = atooct(val) & 0777;
3727                 break;
3728
3729           case O_DLVR_MIN:      /* deliver by minimum time */
3730                 DeliverByMin = convtime(val, 's');
3731                 break;
3732
3733           /* modifiers {daemon_flags} for direct submissions */
3734           case O_DIRECTSUBMODIFIERS:
3735                 {
3736                         BITMAP256 m;    /* ignored */
3737                         extern ENVELOPE BlankEnvelope;
3738
3739                         macdefine(&BlankEnvelope.e_macro, A_PERM,
3740                                   macid("{daemon_flags}"),
3741                                   getmodifiers(val, m));
3742                 }
3743                 break;
3744
3745           case O_FASTSPLIT:
3746                 FastSplit = atoi(val);
3747                 break;
3748
3749           case O_MBDB:
3750                 Mbdb = newstr(val);
3751                 break;
3752
3753           case O_MSQ:
3754                 UseMSP = atobool(val);
3755                 break;
3756
3757           case O_SOFTBOUNCE:
3758                 SoftBounce = atobool(val);
3759                 break;
3760
3761           case O_REJECTLOGINTERVAL:     /* time btwn log msgs while refusing */
3762                 RejectLogInterval = convtime(val, 'h');
3763                 break;
3764
3765           case O_REQUIRES_DIR_FSYNC:
3766 #if REQUIRES_DIR_FSYNC
3767                 RequiresDirfsync = atobool(val);
3768 #else /* REQUIRES_DIR_FSYNC */
3769                 /* silently ignored... required for cf file option */
3770 #endif /* REQUIRES_DIR_FSYNC */
3771                 break;
3772
3773           case O_CONNECTION_RATE_WINDOW_SIZE:
3774                 ConnectionRateWindowSize = convtime(val, 's');
3775                 break;
3776
3777           case O_FALLBACKSMARTHOST:     /* fallback smart host */
3778                 if (val[0] != '\0')
3779                         FallbackSmartHost = newstr(val);
3780                 break;
3781
3782           case O_HELONAME:
3783                 HeloName = newstr(val);
3784                 break;
3785
3786 #if _FFR_MEMSTAT
3787           case O_REFUSELOWMEM:
3788                 RefuseLowMem = atoi(val);
3789                 break;
3790           case O_QUEUELOWMEM:
3791                 QueueLowMem = atoi(val);
3792                 break;
3793           case O_MEMRESOURCE:
3794                 MemoryResource = newstr(val);
3795                 break;
3796 #endif /* _FFR_MEMSTAT */
3797
3798           case O_MAXNOOPCOMMANDS:
3799                 MaxNOOPCommands = atoi(val);
3800                 break;
3801
3802 #if _FFR_MSG_ACCEPT
3803           case O_MSG_ACCEPT:
3804                 MessageAccept = newstr(val);
3805                 break;
3806 #endif /* _FFR_MSG_ACCEPT */
3807
3808 #if _FFR_QUEUE_RUN_PARANOIA
3809           case O_CHK_Q_RUNNERS:
3810                 CheckQueueRunners = atoi(val);
3811                 break;
3812 #endif /* _FFR_QUEUE_RUN_PARANOIA */
3813
3814 #if _FFR_EIGHT_BIT_ADDR_OK
3815           case O_EIGHT_BIT_ADDR_OK:
3816                 EightBitAddrOK = atobool(val);
3817                 break;
3818 #endif /* _FFR_EIGHT_BIT_ADDR_OK */
3819
3820 #if _FFR_ADDR_TYPE_MODES
3821           case O_ADDR_TYPE_MODES:
3822                 AddrTypeModes = atobool(val);
3823                 break;
3824 #endif /* _FFR_ADDR_TYPE_MODES */
3825
3826 #if _FFR_BADRCPT_SHUTDOWN
3827           case O_RCPTSHUTD:
3828                 BadRcptShutdown = atoi(val);
3829                 break;
3830
3831           case O_RCPTSHUTDG:
3832                 BadRcptShutdownGood = atoi(val);
3833                 break;
3834 #endif /* _FFR_BADRCPT_SHUTDOWN */
3835
3836           default:
3837                 if (tTd(37, 1))
3838                 {
3839                         if (isascii(opt) && isprint(opt))
3840                                 sm_dprintf("Warning: option %c unknown\n", opt);
3841                         else
3842                                 sm_dprintf("Warning: option 0x%x unknown\n", opt);
3843                 }
3844                 break;
3845         }
3846
3847         /*
3848         **  Options with suboptions are responsible for taking care
3849         **  of sticky-ness (e.g., that a command line setting is kept
3850         **  when reading in the sendmail.cf file).  This has to be done
3851         **  when the suboptions are parsed since each suboption must be
3852         **  sticky, not the root option.
3853         */
3854
3855         if (sticky && !bitset(OI_SUBOPT, o->o_flags))
3856                 setbitn(opt, StickyOpt);
3857 }
3858 /*
3859 **  SETCLASS -- set a string into a class
3860 **
3861 **      Parameters:
3862 **              class -- the class to put the string in.
3863 **              str -- the string to enter
3864 **
3865 **      Returns:
3866 **              none.
3867 **
3868 **      Side Effects:
3869 **              puts the word into the symbol table.
3870 */
3871
3872 void
3873 setclass(class, str)
3874         int class;
3875         char *str;
3876 {
3877         register STAB *s;
3878
3879         if ((str[0] & 0377) == MATCHCLASS)
3880         {
3881                 int mid;
3882
3883                 str++;
3884                 mid = macid(str);
3885                 if (mid == 0)
3886                         return;
3887
3888                 if (tTd(37, 8))
3889                         sm_dprintf("setclass(%s, $=%s)\n",
3890                                    macname(class), macname(mid));
3891                 copy_class(mid, class);
3892         }
3893         else
3894         {
3895                 if (tTd(37, 8))
3896                         sm_dprintf("setclass(%s, %s)\n", macname(class), str);
3897
3898                 s = stab(str, ST_CLASS, ST_ENTER);
3899                 setbitn(bitidx(class), s->s_class);
3900         }
3901 }
3902 /*
3903 **  MAKEMAPENTRY -- create a map entry
3904 **
3905 **      Parameters:
3906 **              line -- the config file line
3907 **
3908 **      Returns:
3909 **              A pointer to the map that has been created.
3910 **              NULL if there was a syntax error.
3911 **
3912 **      Side Effects:
3913 **              Enters the map into the dictionary.
3914 */
3915
3916 MAP *
3917 makemapentry(line)
3918         char *line;
3919 {
3920         register char *p;
3921         char *mapname;
3922         char *classname;
3923         register STAB *s;
3924         STAB *class;
3925
3926         for (p = line; isascii(*p) && isspace(*p); p++)
3927                 continue;
3928         if (!(isascii(*p) && isalnum(*p)))
3929         {
3930                 syserr("readcf: config K line: no map name");
3931                 return NULL;
3932         }
3933
3934         mapname = p;
3935         while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3936                 continue;
3937         if (*p != '\0')
3938                 *p++ = '\0';
3939         while (isascii(*p) && isspace(*p))
3940                 p++;
3941         if (!(isascii(*p) && isalnum(*p)))
3942         {
3943                 syserr("readcf: config K line, map %s: no map class", mapname);
3944                 return NULL;
3945         }
3946         classname = p;
3947         while (isascii(*++p) && isalnum(*p))
3948                 continue;
3949         if (*p != '\0')
3950                 *p++ = '\0';
3951         while (isascii(*p) && isspace(*p))
3952                 p++;
3953
3954         /* look up the class */
3955         class = stab(classname, ST_MAPCLASS, ST_FIND);
3956         if (class == NULL)
3957         {
3958                 syserr("readcf: map %s: class %s not available", mapname,
3959                         classname);
3960                 return NULL;
3961         }
3962
3963         /* enter the map */
3964         s = stab(mapname, ST_MAP, ST_ENTER);
3965         s->s_map.map_class = &class->s_mapclass;
3966         s->s_map.map_mname = newstr(mapname);
3967
3968         if (class->s_mapclass.map_parse(&s->s_map, p))
3969                 s->s_map.map_mflags |= MF_VALID;
3970
3971         if (tTd(37, 5))
3972         {
3973                 sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
3974                            s->s_map.map_mname, s->s_map.map_class->map_cname,
3975                            s->s_map.map_mflags, s->s_map.map_file);
3976                 sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
3977                            s->s_map.map_app, s->s_map.map_domain,
3978                            s->s_map.map_rebuild);
3979         }
3980         return &s->s_map;
3981 }
3982 /*
3983 **  STRTORWSET -- convert string to rewriting set number
3984 **
3985 **      Parameters:
3986 **              p -- the pointer to the string to decode.
3987 **              endp -- if set, store the trailing delimiter here.
3988 **              stabmode -- ST_ENTER to create this entry, ST_FIND if
3989 **                      it must already exist.
3990 **
3991 **      Returns:
3992 **              The appropriate ruleset number.
3993 **              -1 if it is not valid (error already printed)
3994 */
3995
3996 int
3997 strtorwset(p, endp, stabmode)
3998         char *p;
3999         char **endp;
4000         int stabmode;
4001 {
4002         int ruleset;
4003         static int nextruleset = MAXRWSETS;
4004
4005         while (isascii(*p) && isspace(*p))
4006                 p++;
4007         if (!isascii(*p))
4008         {
4009                 syserr("invalid ruleset name: \"%.20s\"", p);
4010                 return -1;
4011         }
4012         if (isdigit(*p))
4013         {
4014                 ruleset = strtol(p, endp, 10);
4015                 if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4016                 {
4017                         syserr("bad ruleset %d (%d max)",
4018                                 ruleset, MAXRWSETS / 2);
4019                         ruleset = -1;
4020                 }
4021         }
4022         else
4023         {
4024                 STAB *s;
4025                 char delim;
4026                 char *q = NULL;
4027
4028                 q = p;
4029                 while (*p != '\0' && isascii(*p) &&
4030                        (isalnum(*p) || *p == '_'))
4031                         p++;
4032                 if (q == p || !(isascii(*q) && isalpha(*q)))
4033                 {
4034                         /* no valid characters */
4035                         syserr("invalid ruleset name: \"%.20s\"", q);
4036                         return -1;
4037                 }
4038                 while (isascii(*p) && isspace(*p))
4039                         *p++ = '\0';
4040                 delim = *p;
4041                 if (delim != '\0')
4042                         *p = '\0';
4043                 s = stab(q, ST_RULESET, stabmode);
4044                 if (delim != '\0')
4045                         *p = delim;
4046
4047                 if (s == NULL)
4048                         return -1;
4049
4050                 if (stabmode == ST_ENTER && delim == '=')
4051                 {
4052                         while (isascii(*++p) && isspace(*p))
4053                                 continue;
4054                         if (!(isascii(*p) && isdigit(*p)))
4055                         {
4056                                 syserr("bad ruleset definition \"%s\" (number required after `=')", q);
4057                                 ruleset = -1;
4058                         }
4059                         else
4060                         {
4061                                 ruleset = strtol(p, endp, 10);
4062                                 if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4063                                 {
4064                                         syserr("bad ruleset number %d in \"%s\" (%d max)",
4065                                                 ruleset, q, MAXRWSETS / 2);
4066                                         ruleset = -1;
4067                                 }
4068                         }
4069                 }
4070                 else
4071                 {
4072                         if (endp != NULL)
4073                                 *endp = p;
4074                         if (s->s_ruleset >= 0)
4075                                 ruleset = s->s_ruleset;
4076                         else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
4077                         {
4078                                 syserr("%s: too many named rulesets (%d max)",
4079                                         q, MAXRWSETS / 2);
4080                                 ruleset = -1;
4081                         }
4082                 }
4083                 if (s->s_ruleset >= 0 &&
4084                     ruleset >= 0 &&
4085                     ruleset != s->s_ruleset)
4086                 {
4087                         syserr("%s: ruleset changed value (old %d, new %d)",
4088                                 q, s->s_ruleset, ruleset);
4089                         ruleset = s->s_ruleset;
4090                 }
4091                 else if (ruleset >= 0)
4092                 {
4093                         s->s_ruleset = ruleset;
4094                 }
4095                 if (stabmode == ST_ENTER && ruleset >= 0)
4096                 {
4097                         char *h = NULL;
4098
4099                         if (RuleSetNames[ruleset] != NULL)
4100                                 sm_free(RuleSetNames[ruleset]); /* XXX */
4101                         if (delim != '\0' && (h = strchr(q, delim)) != NULL)
4102                                 *h = '\0';
4103                         RuleSetNames[ruleset] = newstr(q);
4104                         if (delim == '/' && h != NULL)
4105                                 *h = delim;     /* put back delim */
4106                 }
4107         }
4108         return ruleset;
4109 }
4110 /*
4111 **  SETTIMEOUT -- set an individual timeout
4112 **
4113 **      Parameters:
4114 **              name -- the name of the timeout.
4115 **              val -- the value of the timeout.
4116 **              sticky -- if set, don't let other setoptions override
4117 **                      this value.
4118 **
4119 **      Returns:
4120 **              none.
4121 */
4122
4123 /* set if Timeout sub-option is stuck */
4124 static BITMAP256        StickyTimeoutOpt;
4125
4126 static struct timeoutinfo
4127 {
4128         char            *to_name;       /* long name of timeout */
4129         unsigned char   to_code;        /* code for option */
4130 } TimeOutTab[] =
4131 {
4132 #define TO_INITIAL                      0x01
4133         { "initial",                    TO_INITIAL                      },
4134 #define TO_MAIL                         0x02
4135         { "mail",                       TO_MAIL                         },
4136 #define TO_RCPT                         0x03
4137         { "rcpt",                       TO_RCPT                         },
4138 #define TO_DATAINIT                     0x04
4139         { "datainit",                   TO_DATAINIT                     },
4140 #define TO_DATABLOCK                    0x05
4141         { "datablock",                  TO_DATABLOCK                    },
4142 #define TO_DATAFINAL                    0x06
4143         { "datafinal",                  TO_DATAFINAL                    },
4144 #define TO_COMMAND                      0x07
4145         { "command",                    TO_COMMAND                      },
4146 #define TO_RSET                         0x08
4147         { "rset",                       TO_RSET                         },
4148 #define TO_HELO                         0x09
4149         { "helo",                       TO_HELO                         },
4150 #define TO_QUIT                         0x0A
4151         { "quit",                       TO_QUIT                         },
4152 #define TO_MISC                         0x0B
4153         { "misc",                       TO_MISC                         },
4154 #define TO_IDENT                        0x0C
4155         { "ident",                      TO_IDENT                        },
4156 #define TO_FILEOPEN                     0x0D
4157         { "fileopen",                   TO_FILEOPEN                     },
4158 #define TO_CONNECT                      0x0E
4159         { "connect",                    TO_CONNECT                      },
4160 #define TO_ICONNECT                     0x0F
4161         { "iconnect",                   TO_ICONNECT                     },
4162 #define TO_QUEUEWARN                    0x10
4163         { "queuewarn",                  TO_QUEUEWARN                    },
4164         { "queuewarn.*",                TO_QUEUEWARN                    },
4165 #define TO_QUEUEWARN_NORMAL             0x11
4166         { "queuewarn.normal",           TO_QUEUEWARN_NORMAL             },
4167 #define TO_QUEUEWARN_URGENT             0x12
4168         { "queuewarn.urgent",           TO_QUEUEWARN_URGENT             },
4169 #define TO_QUEUEWARN_NON_URGENT         0x13
4170         { "queuewarn.non-urgent",       TO_QUEUEWARN_NON_URGENT         },
4171 #define TO_QUEUERETURN                  0x14
4172         { "queuereturn",                TO_QUEUERETURN                  },
4173         { "queuereturn.*",              TO_QUEUERETURN                  },
4174 #define TO_QUEUERETURN_NORMAL           0x15
4175         { "queuereturn.normal",         TO_QUEUERETURN_NORMAL           },
4176 #define TO_QUEUERETURN_URGENT           0x16
4177         { "queuereturn.urgent",         TO_QUEUERETURN_URGENT           },
4178 #define TO_QUEUERETURN_NON_URGENT       0x17
4179         { "queuereturn.non-urgent",     TO_QUEUERETURN_NON_URGENT       },
4180 #define TO_HOSTSTATUS                   0x18
4181         { "hoststatus",                 TO_HOSTSTATUS                   },
4182 #define TO_RESOLVER_RETRANS             0x19
4183         { "resolver.retrans",           TO_RESOLVER_RETRANS             },
4184 #define TO_RESOLVER_RETRANS_NORMAL      0x1A
4185         { "resolver.retrans.normal",    TO_RESOLVER_RETRANS_NORMAL      },
4186 #define TO_RESOLVER_RETRANS_FIRST       0x1B
4187         { "resolver.retrans.first",     TO_RESOLVER_RETRANS_FIRST       },
4188 #define TO_RESOLVER_RETRY               0x1C
4189         { "resolver.retry",             TO_RESOLVER_RETRY               },
4190 #define TO_RESOLVER_RETRY_NORMAL        0x1D
4191         { "resolver.retry.normal",      TO_RESOLVER_RETRY_NORMAL        },
4192 #define TO_RESOLVER_RETRY_FIRST         0x1E
4193         { "resolver.retry.first",       TO_RESOLVER_RETRY_FIRST         },
4194 #define TO_CONTROL                      0x1F
4195         { "control",                    TO_CONTROL                      },
4196 #define TO_LHLO                         0x20
4197         { "lhlo",                       TO_LHLO                         },
4198 #define TO_AUTH                         0x21
4199         { "auth",                       TO_AUTH                         },
4200 #define TO_STARTTLS                     0x22
4201         { "starttls",                   TO_STARTTLS                     },
4202 #define TO_ACONNECT                     0x23
4203         { "aconnect",                   TO_ACONNECT                     },
4204 #define TO_QUEUEWARN_DSN                0x24
4205         { "queuewarn.dsn",              TO_QUEUEWARN_DSN                },
4206 #define TO_QUEUERETURN_DSN              0x25
4207         { "queuereturn.dsn",            TO_QUEUERETURN_DSN              },
4208         { NULL,                         0                               },
4209 };
4210
4211
4212 static void
4213 settimeout(name, val, sticky)
4214         char *name;
4215         char *val;
4216         bool sticky;
4217 {
4218         register struct timeoutinfo *to;
4219         int i, addopts;
4220         time_t toval;
4221
4222         if (tTd(37, 2))
4223                 sm_dprintf("settimeout(%s = %s)", name, val);
4224
4225         for (to = TimeOutTab; to->to_name != NULL; to++)
4226         {
4227                 if (sm_strcasecmp(to->to_name, name) == 0)
4228                         break;
4229         }
4230
4231         if (to->to_name == NULL)
4232         {
4233                 errno = 0; /* avoid bogus error text */
4234                 syserr("settimeout: invalid timeout %s", name);
4235                 return;
4236         }
4237
4238         /*
4239         **  See if this option is preset for us.
4240         */
4241
4242         if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
4243         {
4244                 if (tTd(37, 2))
4245                         sm_dprintf(" (ignored)\n");
4246                 return;
4247         }
4248
4249         if (tTd(37, 2))
4250                 sm_dprintf("\n");
4251
4252         toval = convtime(val, 'm');
4253         addopts = 0;
4254
4255         switch (to->to_code)
4256         {
4257           case TO_INITIAL:
4258                 TimeOuts.to_initial = toval;
4259                 break;
4260
4261           case TO_MAIL:
4262                 TimeOuts.to_mail = toval;
4263                 break;
4264
4265           case TO_RCPT:
4266                 TimeOuts.to_rcpt = toval;
4267                 break;
4268
4269           case TO_DATAINIT:
4270                 TimeOuts.to_datainit = toval;
4271                 break;
4272
4273           case TO_DATABLOCK:
4274                 TimeOuts.to_datablock = toval;
4275                 break;
4276
4277           case TO_DATAFINAL:
4278                 TimeOuts.to_datafinal = toval;
4279                 break;
4280
4281           case TO_COMMAND:
4282                 TimeOuts.to_nextcommand = toval;
4283                 break;
4284
4285           case TO_RSET:
4286                 TimeOuts.to_rset = toval;
4287                 break;
4288
4289           case TO_HELO:
4290                 TimeOuts.to_helo = toval;
4291                 break;
4292
4293           case TO_QUIT:
4294                 TimeOuts.to_quit = toval;
4295                 break;
4296
4297           case TO_MISC:
4298                 TimeOuts.to_miscshort = toval;
4299                 break;
4300
4301           case TO_IDENT:
4302                 TimeOuts.to_ident = toval;
4303                 break;
4304
4305           case TO_FILEOPEN:
4306                 TimeOuts.to_fileopen = toval;
4307                 break;
4308
4309           case TO_CONNECT:
4310                 TimeOuts.to_connect = toval;
4311                 break;
4312
4313           case TO_ICONNECT:
4314                 TimeOuts.to_iconnect = toval;
4315                 break;
4316
4317           case TO_ACONNECT:
4318                 TimeOuts.to_aconnect = toval;
4319                 break;
4320
4321           case TO_QUEUEWARN:
4322                 toval = convtime(val, 'h');
4323                 TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4324                 TimeOuts.to_q_warning[TOC_URGENT] = toval;
4325                 TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4326                 TimeOuts.to_q_warning[TOC_DSN] = toval;
4327                 addopts = 2;
4328                 break;
4329
4330           case TO_QUEUEWARN_NORMAL:
4331                 toval = convtime(val, 'h');
4332                 TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4333                 break;
4334
4335           case TO_QUEUEWARN_URGENT:
4336                 toval = convtime(val, 'h');
4337                 TimeOuts.to_q_warning[TOC_URGENT] = toval;
4338                 break;
4339
4340           case TO_QUEUEWARN_NON_URGENT:
4341                 toval = convtime(val, 'h');
4342                 TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4343                 break;
4344
4345           case TO_QUEUEWARN_DSN:
4346                 toval = convtime(val, 'h');
4347                 TimeOuts.to_q_warning[TOC_DSN] = toval;
4348                 break;
4349
4350           case TO_QUEUERETURN:
4351                 toval = convtime(val, 'd');
4352                 TimeOuts.to_q_return[TOC_NORMAL] = toval;
4353                 TimeOuts.to_q_return[TOC_URGENT] = toval;
4354                 TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4355                 TimeOuts.to_q_return[TOC_DSN] = toval;
4356                 addopts = 2;
4357                 break;
4358
4359           case TO_QUEUERETURN_NORMAL:
4360                 toval = convtime(val, 'd');
4361                 TimeOuts.to_q_return[TOC_NORMAL] = toval;
4362                 break;
4363
4364           case TO_QUEUERETURN_URGENT:
4365                 toval = convtime(val, 'd');
4366                 TimeOuts.to_q_return[TOC_URGENT] = toval;
4367                 break;
4368
4369           case TO_QUEUERETURN_NON_URGENT:
4370                 toval = convtime(val, 'd');
4371                 TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4372                 break;
4373
4374           case TO_QUEUERETURN_DSN:
4375                 toval = convtime(val, 'd');
4376                 TimeOuts.to_q_return[TOC_DSN] = toval;
4377                 break;
4378
4379           case TO_HOSTSTATUS:
4380                 MciInfoTimeout = toval;
4381                 break;
4382
4383           case TO_RESOLVER_RETRANS:
4384                 toval = convtime(val, 's');
4385                 TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
4386                 TimeOuts.res_retrans[RES_TO_FIRST] = toval;
4387                 TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
4388                 addopts = 2;
4389                 break;
4390
4391           case TO_RESOLVER_RETRY:
4392                 i = atoi(val);
4393                 TimeOuts.res_retry[RES_TO_DEFAULT] = i;
4394                 TimeOuts.res_retry[RES_TO_FIRST] = i;
4395                 TimeOuts.res_retry[RES_TO_NORMAL] = i;
4396                 addopts = 2;
4397                 break;
4398
4399           case TO_RESOLVER_RETRANS_NORMAL:
4400                 TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
4401                 break;
4402
4403           case TO_RESOLVER_RETRY_NORMAL:
4404                 TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
4405                 break;
4406
4407           case TO_RESOLVER_RETRANS_FIRST:
4408                 TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
4409                 break;
4410
4411           case TO_RESOLVER_RETRY_FIRST:
4412                 TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
4413                 break;
4414
4415           case TO_CONTROL:
4416                 TimeOuts.to_control = toval;
4417                 break;
4418
4419           case TO_LHLO:
4420                 TimeOuts.to_lhlo = toval;
4421                 break;
4422
4423 #if SASL
4424           case TO_AUTH:
4425                 TimeOuts.to_auth = toval;
4426                 break;
4427 #endif /* SASL */
4428
4429 #if STARTTLS
4430           case TO_STARTTLS:
4431                 TimeOuts.to_starttls = toval;
4432                 break;
4433 #endif /* STARTTLS */
4434
4435           default:
4436                 syserr("settimeout: invalid timeout %s", name);
4437                 break;
4438         }
4439
4440         if (sticky)
4441         {
4442                 for (i = 0; i <= addopts; i++)
4443                         setbitn(to->to_code + i, StickyTimeoutOpt);
4444         }
4445 }
4446 /*
4447 **  INITTIMEOUTS -- parse and set timeout values
4448 **
4449 **      Parameters:
4450 **              val -- a pointer to the values.  If NULL, do initial
4451 **                      settings.
4452 **              sticky -- if set, don't let other setoptions override
4453 **                      this suboption value.
4454 **
4455 **      Returns:
4456 **              none.
4457 **
4458 **      Side Effects:
4459 **              Initializes the TimeOuts structure
4460 */
4461
4462 void
4463 inittimeouts(val, sticky)
4464         register char *val;
4465         bool sticky;
4466 {
4467         register char *p;
4468
4469         if (tTd(37, 2))
4470                 sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
4471         if (val == NULL)
4472         {
4473                 TimeOuts.to_connect = (time_t) 0 SECONDS;
4474                 TimeOuts.to_aconnect = (time_t) 0 SECONDS;
4475                 TimeOuts.to_iconnect = (time_t) 0 SECONDS;
4476                 TimeOuts.to_initial = (time_t) 5 MINUTES;
4477                 TimeOuts.to_helo = (time_t) 5 MINUTES;
4478                 TimeOuts.to_mail = (time_t) 10 MINUTES;
4479                 TimeOuts.to_rcpt = (time_t) 1 HOUR;
4480                 TimeOuts.to_datainit = (time_t) 5 MINUTES;
4481                 TimeOuts.to_datablock = (time_t) 1 HOUR;
4482                 TimeOuts.to_datafinal = (time_t) 1 HOUR;
4483                 TimeOuts.to_rset = (time_t) 5 MINUTES;
4484                 TimeOuts.to_quit = (time_t) 2 MINUTES;
4485                 TimeOuts.to_nextcommand = (time_t) 1 HOUR;
4486                 TimeOuts.to_miscshort = (time_t) 2 MINUTES;
4487 #if IDENTPROTO
4488                 TimeOuts.to_ident = (time_t) 5 SECONDS;
4489 #else /* IDENTPROTO */
4490                 TimeOuts.to_ident = (time_t) 0 SECONDS;
4491 #endif /* IDENTPROTO */
4492                 TimeOuts.to_fileopen = (time_t) 60 SECONDS;
4493                 TimeOuts.to_control = (time_t) 2 MINUTES;
4494                 TimeOuts.to_lhlo = (time_t) 2 MINUTES;
4495 #if SASL
4496                 TimeOuts.to_auth = (time_t) 10 MINUTES;
4497 #endif /* SASL */
4498 #if STARTTLS
4499                 TimeOuts.to_starttls = (time_t) 1 HOUR;
4500 #endif /* STARTTLS */
4501                 if (tTd(37, 5))
4502                 {
4503                         sm_dprintf("Timeouts:\n");
4504                         sm_dprintf("  connect = %ld\n",
4505                                    (long) TimeOuts.to_connect);
4506                         sm_dprintf("  aconnect = %ld\n",
4507                                    (long) TimeOuts.to_aconnect);
4508                         sm_dprintf("  initial = %ld\n",
4509                                    (long) TimeOuts.to_initial);
4510                         sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
4511                         sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
4512                         sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
4513                         sm_dprintf("  datainit = %ld\n",
4514                                    (long) TimeOuts.to_datainit);
4515                         sm_dprintf("  datablock = %ld\n",
4516                                    (long) TimeOuts.to_datablock);
4517                         sm_dprintf("  datafinal = %ld\n",
4518                                    (long) TimeOuts.to_datafinal);
4519                         sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
4520                         sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
4521                         sm_dprintf("  nextcommand = %ld\n",
4522                                    (long) TimeOuts.to_nextcommand);
4523                         sm_dprintf("  miscshort = %ld\n",
4524                                    (long) TimeOuts.to_miscshort);
4525                         sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
4526                         sm_dprintf("  fileopen = %ld\n",
4527                                    (long) TimeOuts.to_fileopen);
4528                         sm_dprintf("  lhlo = %ld\n",
4529                                    (long) TimeOuts.to_lhlo);
4530                         sm_dprintf("  control = %ld\n",
4531                                    (long) TimeOuts.to_control);
4532                 }
4533                 return;
4534         }
4535
4536         for (;; val = p)
4537         {
4538                 while (isascii(*val) && isspace(*val))
4539                         val++;
4540                 if (*val == '\0')
4541                         break;
4542                 for (p = val; *p != '\0' && *p != ','; p++)
4543                         continue;
4544                 if (*p != '\0')
4545                         *p++ = '\0';
4546
4547                 if (isascii(*val) && isdigit(*val))
4548                 {
4549                         /* old syntax -- set everything */
4550                         TimeOuts.to_mail = convtime(val, 'm');
4551                         TimeOuts.to_rcpt = TimeOuts.to_mail;
4552                         TimeOuts.to_datainit = TimeOuts.to_mail;
4553                         TimeOuts.to_datablock = TimeOuts.to_mail;
4554                         TimeOuts.to_datafinal = TimeOuts.to_mail;
4555                         TimeOuts.to_nextcommand = TimeOuts.to_mail;
4556                         if (sticky)
4557                         {
4558                                 setbitn(TO_MAIL, StickyTimeoutOpt);
4559                                 setbitn(TO_RCPT, StickyTimeoutOpt);
4560                                 setbitn(TO_DATAINIT, StickyTimeoutOpt);
4561                                 setbitn(TO_DATABLOCK, StickyTimeoutOpt);
4562                                 setbitn(TO_DATAFINAL, StickyTimeoutOpt);
4563                                 setbitn(TO_COMMAND, StickyTimeoutOpt);
4564                         }
4565                         continue;
4566                 }
4567                 else
4568                 {
4569                         register char *q = strchr(val, ':');
4570
4571                         if (q == NULL && (q = strchr(val, '=')) == NULL)
4572                         {
4573                                 /* syntax error */
4574                                 continue;
4575                         }
4576                         *q++ = '\0';
4577                         settimeout(val, q, sticky);
4578                 }
4579         }
4580 }