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