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