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