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