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