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