]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/readcf.c
This commit was generated by cvs2svn to compensate for changes in r168515,
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / 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.663 2006/10/05 20:58:59 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
2249         { NULL,                         '\0',           OI_NONE }
2250 };
2251
2252 # define CANONIFY(val)
2253
2254 # define SET_OPT_DEFAULT(opt, val)      opt = val
2255
2256 /* set a string option by expanding the value and assigning it */
2257 /* WARNING this belongs ONLY into a case statement! */
2258 #define SET_STRING_EXP(str)     \
2259                 expand(val, exbuf, sizeof(exbuf), e);   \
2260                 newval = sm_pstrdup_x(exbuf);           \
2261                 if (str != NULL)        \
2262                         sm_free(str);   \
2263                 CANONIFY(newval);       \
2264                 str = newval;           \
2265                 break
2266
2267 #define OPTNAME o->o_name == NULL ? "<unknown>" : o->o_name
2268
2269 void
2270 setoption(opt, val, safe, sticky, e)
2271         int opt;
2272         char *val;
2273         bool safe;
2274         bool sticky;
2275         register ENVELOPE *e;
2276 {
2277         register char *p;
2278         register struct optioninfo *o;
2279         char *subopt;
2280         int mid;
2281         bool can_setuid = RunAsUid == 0;
2282         auto char *ep;
2283         char buf[50];
2284         extern bool Warn_Q_option;
2285 #if _FFR_ALLOW_SASLINFO
2286         extern unsigned int SubmitMode;
2287 #endif /* _FFR_ALLOW_SASLINFO */
2288 #if STARTTLS || SM_CONF_SHM
2289         char *newval;
2290         char exbuf[MAXLINE];
2291 #endif /* STARTTLS || SM_CONF_SHM */
2292
2293         errno = 0;
2294         if (opt == ' ')
2295         {
2296                 /* full word options */
2297                 struct optioninfo *sel;
2298
2299                 p = strchr(val, '=');
2300                 if (p == NULL)
2301                         p = &val[strlen(val)];
2302                 while (*--p == ' ')
2303                         continue;
2304                 while (*++p == ' ')
2305                         *p = '\0';
2306                 if (p == val)
2307                 {
2308                         syserr("readcf: null option name");
2309                         return;
2310                 }
2311                 if (*p == '=')
2312                         *p++ = '\0';
2313                 while (*p == ' ')
2314                         p++;
2315                 subopt = strchr(val, '.');
2316                 if (subopt != NULL)
2317                         *subopt++ = '\0';
2318                 sel = NULL;
2319                 for (o = OptionTab; o->o_name != NULL; o++)
2320                 {
2321                         if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
2322                                 continue;
2323                         if (strlen(o->o_name) == strlen(val))
2324                         {
2325                                 /* completely specified -- this must be it */
2326                                 sel = NULL;
2327                                 break;
2328                         }
2329                         if (sel != NULL)
2330                                 break;
2331                         sel = o;
2332                 }
2333                 if (sel != NULL && o->o_name == NULL)
2334                         o = sel;
2335                 else if (o->o_name == NULL)
2336                 {
2337                         syserr("readcf: unknown option name %s", val);
2338                         return;
2339                 }
2340                 else if (sel != NULL)
2341                 {
2342                         syserr("readcf: ambiguous option name %s (matches %s and %s)",
2343                                 val, sel->o_name, o->o_name);
2344                         return;
2345                 }
2346                 if (strlen(val) != strlen(o->o_name))
2347                 {
2348                         int oldVerbose = Verbose;
2349
2350                         Verbose = 1;
2351                         message("Option %s used as abbreviation for %s",
2352                                 val, o->o_name);
2353                         Verbose = oldVerbose;
2354                 }
2355                 opt = o->o_code;
2356                 val = p;
2357         }
2358         else
2359         {
2360                 for (o = OptionTab; o->o_name != NULL; o++)
2361                 {
2362                         if (o->o_code == opt)
2363                                 break;
2364                 }
2365                 if (o->o_name == NULL)
2366                 {
2367                         syserr("readcf: unknown option name 0x%x", opt & 0xff);
2368                         return;
2369                 }
2370                 subopt = NULL;
2371         }
2372
2373         if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
2374         {
2375                 if (tTd(37, 1))
2376                         sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
2377                                    OPTNAME, subopt);
2378                 subopt = NULL;
2379         }
2380
2381         if (tTd(37, 1))
2382         {
2383                 sm_dprintf(isascii(opt) && isprint(opt) ?
2384                            "setoption %s (%c)%s%s=" :
2385                            "setoption %s (0x%x)%s%s=",
2386                            OPTNAME, opt, subopt == NULL ? "" : ".",
2387                            subopt == NULL ? "" : subopt);
2388                 xputs(sm_debug_file(), val);
2389         }
2390
2391         /*
2392         **  See if this option is preset for us.
2393         */
2394
2395         if (!sticky && bitnset(opt, StickyOpt))
2396         {
2397                 if (tTd(37, 1))
2398                         sm_dprintf(" (ignored)\n");
2399                 return;
2400         }
2401
2402         /*
2403         **  Check to see if this option can be specified by this user.
2404         */
2405
2406         if (!safe && RealUid == 0)
2407                 safe = true;
2408         if (!safe && !bitset(OI_SAFE, o->o_flags))
2409         {
2410                 if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
2411                 {
2412                         int dp;
2413
2414                         if (tTd(37, 1))
2415                                 sm_dprintf(" (unsafe)");
2416                         dp = drop_privileges(true);
2417                         setstat(dp);
2418                 }
2419         }
2420         if (tTd(37, 1))
2421                 sm_dprintf("\n");
2422
2423         switch (opt & 0xff)
2424         {
2425           case '7':             /* force seven-bit input */
2426                 SevenBitInput = atobool(val);
2427                 break;
2428
2429           case '8':             /* handling of 8-bit input */
2430 #if MIME8TO7
2431                 switch (*val)
2432                 {
2433                   case 'p':             /* pass 8 bit, convert MIME */
2434                         MimeMode = MM_CVTMIME|MM_PASS8BIT;
2435                         break;
2436
2437                   case 'm':             /* convert 8-bit, convert MIME */
2438                         MimeMode = MM_CVTMIME|MM_MIME8BIT;
2439                         break;
2440
2441                   case 's':             /* strict adherence */
2442                         MimeMode = MM_CVTMIME;
2443                         break;
2444
2445 # if 0
2446                   case 'r':             /* reject 8-bit, don't convert MIME */
2447                         MimeMode = 0;
2448                         break;
2449
2450                   case 'j':             /* "just send 8" */
2451                         MimeMode = MM_PASS8BIT;
2452                         break;
2453
2454                   case 'a':             /* encode 8 bit if available */
2455                         MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
2456                         break;
2457
2458                   case 'c':             /* convert 8 bit to MIME, never 7 bit */
2459                         MimeMode = MM_MIME8BIT;
2460                         break;
2461 # endif /* 0 */
2462
2463                   default:
2464                         syserr("Unknown 8-bit mode %c", *val);
2465                         finis(false, true, EX_USAGE);
2466                 }
2467 #else /* MIME8TO7 */
2468                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2469                                      "Warning: Option: %s requires MIME8TO7 support\n",
2470                                      OPTNAME);
2471 #endif /* MIME8TO7 */
2472                 break;
2473
2474           case 'A':             /* set default alias file */
2475                 if (val[0] == '\0')
2476                 {
2477                         char *al;
2478
2479                         SET_OPT_DEFAULT(al, "aliases");
2480                         setalias(al);
2481                 }
2482                 else
2483                         setalias(val);
2484                 break;
2485
2486           case 'a':             /* look N minutes for "@:@" in alias file */
2487                 if (val[0] == '\0')
2488                         SafeAlias = 5 MINUTES;
2489                 else
2490                         SafeAlias = convtime(val, 'm');
2491                 break;
2492
2493           case 'B':             /* substitution for blank character */
2494                 SpaceSub = val[0];
2495                 if (SpaceSub == '\0')
2496                         SpaceSub = ' ';
2497                 break;
2498
2499           case 'b':             /* min blocks free on queue fs/max msg size */
2500                 p = strchr(val, '/');
2501                 if (p != NULL)
2502                 {
2503                         *p++ = '\0';
2504                         MaxMessageSize = atol(p);
2505                 }
2506                 MinBlocksFree = atol(val);
2507                 break;
2508
2509           case 'c':             /* don't connect to "expensive" mailers */
2510                 NoConnect = atobool(val);
2511                 break;
2512
2513           case 'C':             /* checkpoint every N addresses */
2514                 if (safe || CheckpointInterval > atoi(val))
2515                         CheckpointInterval = atoi(val);
2516                 break;
2517
2518           case 'd':             /* delivery mode */
2519                 switch (*val)
2520                 {
2521                   case '\0':
2522                         set_delivery_mode(SM_DELIVER, e);
2523                         break;
2524
2525                   case SM_QUEUE:        /* queue only */
2526                   case SM_DEFER:        /* queue only and defer map lookups */
2527                   case SM_DELIVER:      /* do everything */
2528                   case SM_FORK:         /* fork after verification */
2529 #if _FFR_DM_ONE
2530                 /* deliver first TA in background, then queue */
2531                   case SM_DM_ONE:
2532 #endif /* _FFR_DM_ONE */
2533                         set_delivery_mode(*val, e);
2534                         break;
2535
2536                   default:
2537                         syserr("Unknown delivery mode %c", *val);
2538                         finis(false, true, EX_USAGE);
2539                 }
2540                 break;
2541
2542           case 'E':             /* error message header/header file */
2543                 if (*val != '\0')
2544                         ErrMsgFile = newstr(val);
2545                 break;
2546
2547           case 'e':             /* set error processing mode */
2548                 switch (*val)
2549                 {
2550                   case EM_QUIET:        /* be silent about it */
2551                   case EM_MAIL:         /* mail back */
2552                   case EM_BERKNET:      /* do berknet error processing */
2553                   case EM_WRITE:        /* write back (or mail) */
2554                   case EM_PRINT:        /* print errors normally (default) */
2555                         e->e_errormode = *val;
2556                         break;
2557                 }
2558                 break;
2559
2560           case 'F':             /* file mode */
2561                 FileMode = atooct(val) & 0777;
2562                 break;
2563
2564           case 'f':             /* save Unix-style From lines on front */
2565                 SaveFrom = atobool(val);
2566                 break;
2567
2568           case 'G':             /* match recipients against GECOS field */
2569                 MatchGecos = atobool(val);
2570                 break;
2571
2572           case 'g':             /* default gid */
2573   g_opt:
2574                 if (isascii(*val) && isdigit(*val))
2575                         DefGid = atoi(val);
2576                 else
2577                 {
2578                         register struct group *gr;
2579
2580                         DefGid = -1;
2581                         gr = getgrnam(val);
2582                         if (gr == NULL)
2583                                 syserr("readcf: option %c: unknown group %s",
2584                                         opt, val);
2585                         else
2586                                 DefGid = gr->gr_gid;
2587                 }
2588                 break;
2589
2590           case 'H':             /* help file */
2591                 if (val[0] == '\0')
2592                 {
2593                         SET_OPT_DEFAULT(HelpFile, "helpfile");
2594                 }
2595                 else
2596                 {
2597                         CANONIFY(val);
2598                         HelpFile = newstr(val);
2599                 }
2600                 break;
2601
2602           case 'h':             /* maximum hop count */
2603                 MaxHopCount = atoi(val);
2604                 break;
2605
2606           case 'I':             /* use internet domain name server */
2607 #if NAMED_BIND
2608                 for (p = val; *p != 0; )
2609                 {
2610                         bool clearmode;
2611                         char *q;
2612                         struct resolverflags *rfp;
2613
2614                         while (*p == ' ')
2615                                 p++;
2616                         if (*p == '\0')
2617                                 break;
2618                         clearmode = false;
2619                         if (*p == '-')
2620                                 clearmode = true;
2621                         else if (*p != '+')
2622                                 p--;
2623                         p++;
2624                         q = p;
2625                         while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2626                                 p++;
2627                         if (*p != '\0')
2628                                 *p++ = '\0';
2629                         if (sm_strcasecmp(q, "HasWildcardMX") == 0)
2630                         {
2631                                 HasWildcardMX = !clearmode;
2632                                 continue;
2633                         }
2634                         if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
2635                         {
2636                                 WorkAroundBrokenAAAA = !clearmode;
2637                                 continue;
2638                         }
2639                         for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
2640                         {
2641                                 if (sm_strcasecmp(q, rfp->rf_name) == 0)
2642                                         break;
2643                         }
2644                         if (rfp->rf_name == NULL)
2645                                 syserr("readcf: I option value %s unrecognized", q);
2646                         else if (clearmode)
2647                                 _res.options &= ~rfp->rf_bits;
2648                         else
2649                                 _res.options |= rfp->rf_bits;
2650                 }
2651                 if (tTd(8, 2))
2652                         sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
2653                                    (unsigned int) _res.options, HasWildcardMX);
2654 #else /* NAMED_BIND */
2655                 usrerr("name server (I option) specified but BIND not compiled in");
2656 #endif /* NAMED_BIND */
2657                 break;
2658
2659           case 'i':             /* ignore dot lines in message */
2660                 IgnrDot = atobool(val);
2661                 break;
2662
2663           case 'j':             /* send errors in MIME (RFC 1341) format */
2664                 SendMIMEErrors = atobool(val);
2665                 break;
2666
2667           case 'J':             /* .forward search path */
2668                 CANONIFY(val);
2669                 ForwardPath = newstr(val);
2670                 break;
2671
2672           case 'k':             /* connection cache size */
2673                 MaxMciCache = atoi(val);
2674                 if (MaxMciCache < 0)
2675                         MaxMciCache = 0;
2676                 break;
2677
2678           case 'K':             /* connection cache timeout */
2679                 MciCacheTimeout = convtime(val, 'm');
2680                 break;
2681
2682           case 'l':             /* use Errors-To: header */
2683                 UseErrorsTo = atobool(val);
2684                 break;
2685
2686           case 'L':             /* log level */
2687                 if (safe || LogLevel < atoi(val))
2688                         LogLevel = atoi(val);
2689                 break;
2690
2691           case 'M':             /* define macro */
2692                 sticky = false;
2693                 mid = macid_parse(val, &ep);
2694                 if (mid == 0)
2695                         break;
2696                 p = newstr(ep);
2697                 if (!safe)
2698                         cleanstrcpy(p, p, strlen(p) + 1);
2699                 macdefine(&CurEnv->e_macro, A_TEMP, mid, p);
2700                 break;
2701
2702           case 'm':             /* send to me too */
2703                 MeToo = atobool(val);
2704                 break;
2705
2706           case 'n':             /* validate RHS in newaliases */
2707                 CheckAliases = atobool(val);
2708                 break;
2709
2710             /* 'N' available -- was "net name" */
2711
2712           case 'O':             /* daemon options */
2713                 if (!setdaemonoptions(val))
2714                         syserr("too many daemons defined (%d max)", MAXDAEMONS);
2715                 break;
2716
2717           case 'o':             /* assume old style headers */
2718                 if (atobool(val))
2719                         CurEnv->e_flags |= EF_OLDSTYLE;
2720                 else
2721                         CurEnv->e_flags &= ~EF_OLDSTYLE;
2722                 break;
2723
2724           case 'p':             /* select privacy level */
2725                 p = val;
2726                 for (;;)
2727                 {
2728                         register struct prival *pv;
2729                         extern struct prival PrivacyValues[];
2730
2731                         while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2732                                 p++;
2733                         if (*p == '\0')
2734                                 break;
2735                         val = p;
2736                         while (isascii(*p) && isalnum(*p))
2737                                 p++;
2738                         if (*p != '\0')
2739                                 *p++ = '\0';
2740
2741                         for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
2742                         {
2743                                 if (sm_strcasecmp(val, pv->pv_name) == 0)
2744                                         break;
2745                         }
2746                         if (pv->pv_name == NULL)
2747                                 syserr("readcf: Op line: %s unrecognized", val);
2748                         else
2749                                 PrivacyFlags |= pv->pv_flag;
2750                 }
2751                 sticky = false;
2752                 break;
2753
2754           case 'P':             /* postmaster copy address for returned mail */
2755                 PostMasterCopy = newstr(val);
2756                 break;
2757
2758           case 'q':             /* slope of queue only function */
2759                 QueueFactor = atoi(val);
2760                 break;
2761
2762           case 'Q':             /* queue directory */
2763                 if (val[0] == '\0')
2764                 {
2765                         QueueDir = "mqueue";
2766                 }
2767                 else
2768                 {
2769                         QueueDir = newstr(val);
2770                 }
2771                 if (RealUid != 0 && !safe)
2772                         Warn_Q_option = true;
2773                 break;
2774
2775           case 'R':             /* don't prune routes */
2776                 DontPruneRoutes = atobool(val);
2777                 break;
2778
2779           case 'r':             /* read timeout */
2780                 if (subopt == NULL)
2781                         inittimeouts(val, sticky);
2782                 else
2783                         settimeout(subopt, val, sticky);
2784                 break;
2785
2786           case 'S':             /* status file */
2787                 if (val[0] == '\0')
2788                 {
2789                         SET_OPT_DEFAULT(StatFile, "statistics");
2790                 }
2791                 else
2792                 {
2793                         CANONIFY(val);
2794                         StatFile = newstr(val);
2795                 }
2796                 break;
2797
2798           case 's':             /* be super safe, even if expensive */
2799                 if (tolower(*val) == 'i')
2800                         SuperSafe = SAFE_INTERACTIVE;
2801                 else if (tolower(*val) == 'p')
2802 #if MILTER
2803                         SuperSafe = SAFE_REALLY_POSTMILTER;
2804 #else /* MILTER */
2805                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2806                                 "Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
2807 #endif /* MILTER */
2808                 else
2809                         SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
2810                 break;
2811
2812           case 'T':             /* queue timeout */
2813                 p = strchr(val, '/');
2814                 if (p != NULL)
2815                 {
2816                         *p++ = '\0';
2817                         settimeout("queuewarn", p, sticky);
2818                 }
2819                 settimeout("queuereturn", val, sticky);
2820                 break;
2821
2822           case 't':             /* time zone name */
2823                 TimeZoneSpec = newstr(val);
2824                 break;
2825
2826           case 'U':             /* location of user database */
2827                 UdbSpec = newstr(val);
2828                 break;
2829
2830           case 'u':             /* set default uid */
2831                 for (p = val; *p != '\0'; p++)
2832                 {
2833 # if _FFR_DOTTED_USERNAMES
2834                         if (*p == '/' || *p == ':')
2835 # else /* _FFR_DOTTED_USERNAMES */
2836                         if (*p == '.' || *p == '/' || *p == ':')
2837 # endif /* _FFR_DOTTED_USERNAMES */
2838                         {
2839                                 *p++ = '\0';
2840                                 break;
2841                         }
2842                 }
2843                 if (isascii(*val) && isdigit(*val))
2844                 {
2845                         DefUid = atoi(val);
2846                         setdefuser();
2847                 }
2848                 else
2849                 {
2850                         register struct passwd *pw;
2851
2852                         DefUid = -1;
2853                         pw = sm_getpwnam(val);
2854                         if (pw == NULL)
2855                         {
2856                                 syserr("readcf: option u: unknown user %s", val);
2857                                 break;
2858                         }
2859                         else
2860                         {
2861                                 DefUid = pw->pw_uid;
2862                                 DefGid = pw->pw_gid;
2863                                 DefUser = newstr(pw->pw_name);
2864                         }
2865                 }
2866
2867 # ifdef UID_MAX
2868                 if (DefUid > UID_MAX)
2869                 {
2870                         syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2871                                 (long)DefUid, (long)UID_MAX);
2872                         break;
2873                 }
2874 # endif /* UID_MAX */
2875
2876                 /* handle the group if it is there */
2877                 if (*p == '\0')
2878                         break;
2879                 val = p;
2880                 goto g_opt;
2881
2882           case 'V':             /* fallback MX host */
2883                 if (val[0] != '\0')
2884                         FallbackMX = newstr(val);
2885                 break;
2886
2887           case 'v':             /* run in verbose mode */
2888                 Verbose = atobool(val) ? 1 : 0;
2889                 break;
2890
2891           case 'w':             /* if we are best MX, try host directly */
2892                 TryNullMXList = atobool(val);
2893                 break;
2894
2895             /* 'W' available -- was wizard password */
2896
2897           case 'x':             /* load avg at which to auto-queue msgs */
2898                 QueueLA = atoi(val);
2899                 break;
2900
2901           case 'X':     /* load avg at which to auto-reject connections */
2902                 RefuseLA = atoi(val);
2903                 break;
2904
2905           case O_DELAY_LA:      /* load avg at which to delay connections */
2906                 DelayLA = atoi(val);
2907                 break;
2908
2909           case 'y':             /* work recipient factor */
2910                 WkRecipFact = atoi(val);
2911                 break;
2912
2913           case 'Y':             /* fork jobs during queue runs */
2914                 ForkQueueRuns = atobool(val);
2915                 break;
2916
2917           case 'z':             /* work message class factor */
2918                 WkClassFact = atoi(val);
2919                 break;
2920
2921           case 'Z':             /* work time factor */
2922                 WkTimeFact = atoi(val);
2923                 break;
2924
2925
2926 #if _FFR_QUEUE_GROUP_SORTORDER
2927         /* coordinate this with makequeue() */
2928 #endif /* _FFR_QUEUE_GROUP_SORTORDER */
2929           case O_QUEUESORTORD:  /* queue sorting order */
2930                 switch (*val)
2931                 {
2932                   case 'f':     /* File Name */
2933                   case 'F':
2934                         QueueSortOrder = QSO_BYFILENAME;
2935                         break;
2936
2937                   case 'h':     /* Host first */
2938                   case 'H':
2939                         QueueSortOrder = QSO_BYHOST;
2940                         break;
2941
2942                   case 'm':     /* Modification time */
2943                   case 'M':
2944                         QueueSortOrder = QSO_BYMODTIME;
2945                         break;
2946
2947                   case 'p':     /* Priority order */
2948                   case 'P':
2949                         QueueSortOrder = QSO_BYPRIORITY;
2950                         break;
2951
2952                   case 't':     /* Submission time */
2953                   case 'T':
2954                         QueueSortOrder = QSO_BYTIME;
2955                         break;
2956
2957                   case 'r':     /* Random */
2958                   case 'R':
2959                         QueueSortOrder = QSO_RANDOM;
2960                         break;
2961
2962 #if _FFR_RHS
2963                   case 's':     /* Shuffled host name */
2964                   case 'S':
2965                         QueueSortOrder = QSO_BYSHUFFLE;
2966                         break;
2967 #endif /* _FFR_RHS */
2968
2969                   case 'n':     /* none */
2970                   case 'N':
2971                         QueueSortOrder = QSO_NONE;
2972                         break;
2973
2974                   default:
2975                         syserr("Invalid queue sort order \"%s\"", val);
2976                 }
2977                 break;
2978
2979           case O_HOSTSFILE:     /* pathname of /etc/hosts file */
2980                 CANONIFY(val);
2981                 HostsFile = newstr(val);
2982                 break;
2983
2984           case O_MQA:           /* minimum queue age between deliveries */
2985                 MinQueueAge = convtime(val, 'm');
2986                 break;
2987
2988           case O_DEFCHARSET:    /* default character set for mimefying */
2989                 DefaultCharSet = newstr(denlstring(val, true, true));
2990                 break;
2991
2992           case O_SSFILE:        /* service switch file */
2993                 CANONIFY(val);
2994                 ServiceSwitchFile = newstr(val);
2995                 break;
2996
2997           case O_DIALDELAY:     /* delay for dial-on-demand operation */
2998                 DialDelay = convtime(val, 's');
2999                 break;
3000
3001           case O_NORCPTACTION:  /* what to do if no recipient */
3002                 if (sm_strcasecmp(val, "none") == 0)
3003                         NoRecipientAction = NRA_NO_ACTION;
3004                 else if (sm_strcasecmp(val, "add-to") == 0)
3005                         NoRecipientAction = NRA_ADD_TO;
3006                 else if (sm_strcasecmp(val, "add-apparently-to") == 0)
3007                         NoRecipientAction = NRA_ADD_APPARENTLY_TO;
3008                 else if (sm_strcasecmp(val, "add-bcc") == 0)
3009                         NoRecipientAction = NRA_ADD_BCC;
3010                 else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
3011                         NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
3012                 else
3013                         syserr("Invalid NoRecipientAction: %s", val);
3014                 break;
3015
3016           case O_SAFEFILEENV:   /* chroot() environ for writing to files */
3017                 if (*val == '\0')
3018                         break;
3019
3020                 /* strip trailing slashes */
3021                 p = val + strlen(val) - 1;
3022                 while (p >= val && *p == '/')
3023                         *p-- = '\0';
3024
3025                 if (*val == '\0')
3026                         break;
3027
3028                 SafeFileEnv = newstr(val);
3029                 break;
3030
3031           case O_MAXMSGSIZE:    /* maximum message size */
3032                 MaxMessageSize = atol(val);
3033                 break;
3034
3035           case O_COLONOKINADDR: /* old style handling of colon addresses */
3036                 ColonOkInAddr = atobool(val);
3037                 break;
3038
3039           case O_MAXQUEUERUN:   /* max # of jobs in a single queue run */
3040                 MaxQueueRun = atoi(val);
3041                 break;
3042
3043           case O_MAXCHILDREN:   /* max # of children of daemon */
3044                 MaxChildren = atoi(val);
3045                 break;
3046
3047           case O_MAXQUEUECHILDREN: /* max # of children of daemon */
3048                 MaxQueueChildren = atoi(val);
3049                 break;
3050
3051           case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
3052                 MaxRunnersPerQueue = atoi(val);
3053                 break;
3054
3055           case O_NICEQUEUERUN:          /* nice queue runs */
3056 #if !HASNICE
3057                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3058                                      "Warning: NiceQueueRun set on system that doesn't support nice()\n");
3059 #endif /* !HASNICE */
3060
3061                 /* XXX do we want to check the range? > 0 ? */
3062                 NiceQueueRun = atoi(val);
3063                 break;
3064
3065           case O_SHMKEY:                /* shared memory key */
3066 #if SM_CONF_SHM
3067                 ShmKey = atol(val);
3068 #else /* SM_CONF_SHM */
3069                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3070                                      "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3071                                      OPTNAME);
3072 #endif /* SM_CONF_SHM */
3073                 break;
3074
3075           case O_SHMKEYFILE:            /* shared memory key file */
3076 #if SM_CONF_SHM
3077                 SET_STRING_EXP(ShmKeyFile);
3078 #else /* SM_CONF_SHM */
3079                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3080                                      "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3081                                      OPTNAME);
3082                 break;
3083 #endif /* SM_CONF_SHM */
3084
3085 #if _FFR_MAX_FORWARD_ENTRIES
3086           case O_MAXFORWARD:    /* max # of forward entries */
3087                 MaxForwardEntries = atoi(val);
3088                 break;
3089 #endif /* _FFR_MAX_FORWARD_ENTRIES */
3090
3091           case O_KEEPCNAMES:    /* don't expand CNAME records */
3092                 DontExpandCnames = atobool(val);
3093                 break;
3094
3095           case O_MUSTQUOTE:     /* must quote these characters in phrases */
3096                 (void) sm_strlcpy(buf, "@,;:\\()[]", sizeof(buf));
3097                 if (strlen(val) < sizeof(buf) - 10)
3098                         (void) sm_strlcat(buf, val, sizeof(buf));
3099                 else
3100                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3101                                              "Warning: MustQuoteChars too long, ignored.\n");
3102                 MustQuoteChars = newstr(buf);
3103                 break;
3104
3105           case O_SMTPGREETING:  /* SMTP greeting message (old $e macro) */
3106                 SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
3107                 break;
3108
3109           case O_UNIXFROM:      /* UNIX From_ line (old $l macro) */
3110                 UnixFromLine = newstr(munchstring(val, NULL, '\0'));
3111                 break;
3112
3113           case O_OPCHARS:       /* operator characters (old $o macro) */
3114                 if (OperatorChars != NULL)
3115                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3116                                              "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
3117                 OperatorChars = newstr(munchstring(val, NULL, '\0'));
3118                 break;
3119
3120           case O_DONTINITGRPS:  /* don't call initgroups(3) */
3121                 DontInitGroups = atobool(val);
3122                 break;
3123
3124           case O_SLFH:          /* make sure from fits on one line */
3125                 SingleLineFromHeader = atobool(val);
3126                 break;
3127
3128           case O_ABH:           /* allow HELO commands with syntax errors */
3129                 AllowBogusHELO = atobool(val);
3130                 break;
3131
3132           case O_CONNTHROT:     /* connection rate throttle */
3133                 ConnRateThrottle = atoi(val);
3134                 break;
3135
3136           case O_UGW:           /* group writable files are unsafe */
3137                 if (!atobool(val))
3138                 {
3139                         setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
3140                                 DontBlameSendmail);
3141                         setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
3142                                 DontBlameSendmail);
3143                 }
3144                 break;
3145
3146           case O_DBLBOUNCE:     /* address to which to send double bounces */
3147                 DoubleBounceAddr = newstr(val);
3148                 break;
3149
3150           case O_HSDIR:         /* persistent host status directory */
3151                 if (val[0] != '\0')
3152                 {
3153                         CANONIFY(val);
3154                         HostStatDir = newstr(val);
3155                 }
3156                 break;
3157
3158           case O_SINGTHREAD:    /* single thread deliveries (requires hsdir) */
3159                 SingleThreadDelivery = atobool(val);
3160                 break;
3161
3162           case O_RUNASUSER:     /* run bulk of code as this user */
3163                 for (p = val; *p != '\0'; p++)
3164                 {
3165 # if _FFR_DOTTED_USERNAMES
3166                         if (*p == '/' || *p == ':')
3167 # else /* _FFR_DOTTED_USERNAMES */
3168                         if (*p == '.' || *p == '/' || *p == ':')
3169 # endif /* _FFR_DOTTED_USERNAMES */
3170                         {
3171                                 *p++ = '\0';
3172                                 break;
3173                         }
3174                 }
3175                 if (isascii(*val) && isdigit(*val))
3176                 {
3177                         if (can_setuid)
3178                                 RunAsUid = atoi(val);
3179                 }
3180                 else
3181                 {
3182                         register struct passwd *pw;
3183
3184                         pw = sm_getpwnam(val);
3185                         if (pw == NULL)
3186                         {
3187                                 syserr("readcf: option RunAsUser: unknown user %s", val);
3188                                 break;
3189                         }
3190                         else if (can_setuid)
3191                         {
3192                                 if (*p == '\0')
3193                                         RunAsUserName = newstr(val);
3194                                 RunAsUid = pw->pw_uid;
3195                                 RunAsGid = pw->pw_gid;
3196                         }
3197                         else if (EffGid == pw->pw_gid)
3198                                 RunAsGid = pw->pw_gid;
3199                         else if (UseMSP && *p == '\0')
3200                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3201                                                      "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3202                                                      (int) EffGid,
3203                                                      (int) pw->pw_gid);
3204                 }
3205 # ifdef UID_MAX
3206                 if (RunAsUid > UID_MAX)
3207                 {
3208                         syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3209                                 (long) RunAsUid, (long) UID_MAX);
3210                         break;
3211                 }
3212 # endif /* UID_MAX */
3213                 if (*p != '\0')
3214                 {
3215                         if (isascii(*p) && isdigit(*p))
3216                         {
3217                                 gid_t runasgid;
3218
3219                                 runasgid = (gid_t) atoi(p);
3220                                 if (can_setuid || EffGid == runasgid)
3221                                         RunAsGid = runasgid;
3222                                 else if (UseMSP)
3223                                         (void) sm_io_fprintf(smioout,
3224                                                              SM_TIME_DEFAULT,
3225                                                              "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3226                                                              (int) EffGid,
3227                                                              (int) runasgid);
3228                         }
3229                         else
3230                         {
3231                                 register struct group *gr;
3232
3233                                 gr = getgrnam(p);
3234                                 if (gr == NULL)
3235                                         syserr("readcf: option RunAsUser: unknown group %s",
3236                                                 p);
3237                                 else if (can_setuid || EffGid == gr->gr_gid)
3238                                         RunAsGid = gr->gr_gid;
3239                                 else if (UseMSP)
3240                                         (void) sm_io_fprintf(smioout,
3241                                                              SM_TIME_DEFAULT,
3242                                                              "WARNING: RunAsUser for MSP ignored, check group ids (egid=%d, want=%d)\n",
3243                                                              (int) EffGid,
3244                                                              (int) gr->gr_gid);
3245                         }
3246                 }
3247                 if (tTd(47, 5))
3248                         sm_dprintf("readcf: RunAsUser = %d:%d\n",
3249                                    (int) RunAsUid, (int) RunAsGid);
3250                 break;
3251
3252           case O_DSN_RRT:
3253                 RrtImpliesDsn = atobool(val);
3254                 break;
3255
3256           case O_PIDFILE:
3257                 PSTRSET(PidFile, val);
3258                 break;
3259
3260           case O_DONTBLAMESENDMAIL:
3261                 p = val;
3262                 for (;;)
3263                 {
3264                         register struct dbsval *dbs;
3265                         extern struct dbsval DontBlameSendmailValues[];
3266
3267                         while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3268                                 p++;
3269                         if (*p == '\0')
3270                                 break;
3271                         val = p;
3272                         while (isascii(*p) && isalnum(*p))
3273                                 p++;
3274                         if (*p != '\0')
3275                                 *p++ = '\0';
3276
3277                         for (dbs = DontBlameSendmailValues;
3278                              dbs->dbs_name != NULL; dbs++)
3279                         {
3280                                 if (sm_strcasecmp(val, dbs->dbs_name) == 0)
3281                                         break;
3282                         }
3283                         if (dbs->dbs_name == NULL)
3284                                 syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
3285                         else if (dbs->dbs_flag == DBS_SAFE)
3286                                 clrbitmap(DontBlameSendmail);
3287                         else
3288                                 setbitn(dbs->dbs_flag, DontBlameSendmail);
3289                 }
3290                 sticky = false;
3291                 break;
3292
3293           case O_DPI:
3294                 if (sm_strcasecmp(val, "loopback") == 0)
3295                         DontProbeInterfaces = DPI_SKIPLOOPBACK;
3296                 else if (atobool(val))
3297                         DontProbeInterfaces = DPI_PROBENONE;
3298                 else
3299                         DontProbeInterfaces = DPI_PROBEALL;
3300                 break;
3301
3302           case O_MAXRCPT:
3303                 MaxRcptPerMsg = atoi(val);
3304                 break;
3305
3306           case O_RCPTTHROT:
3307                 BadRcptThrottle = atoi(val);
3308                 break;
3309
3310           case O_DEADLETTER:
3311                 CANONIFY(val);
3312                 PSTRSET(DeadLetterDrop, val);
3313                 break;
3314
3315 #if _FFR_DONTLOCKFILESFORREAD_OPTION
3316           case O_DONTLOCK:
3317                 DontLockReadFiles = atobool(val);
3318                 break;
3319 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
3320
3321           case O_MAXALIASRCSN:
3322                 MaxAliasRecursion = atoi(val);
3323                 break;
3324
3325           case O_CNCTONLYTO:
3326                 /* XXX should probably use gethostbyname */
3327 #if NETINET || NETINET6
3328                 ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
3329 # if NETINET6
3330                 if (anynet_pton(AF_INET6, val,
3331                                 &ConnectOnlyTo.sin6.sin6_addr) != 1)
3332                         ConnectOnlyTo.sa.sa_family = AF_INET6;
3333                 else
3334 # endif /* NETINET6 */
3335 # if NETINET
3336                 {
3337                         ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
3338                         if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
3339                                 ConnectOnlyTo.sa.sa_family = AF_INET;
3340                 }
3341
3342 # endif /* NETINET */
3343                 if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
3344                 {
3345                         syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
3346                                val);
3347                         break;
3348                 }
3349 #endif /* NETINET || NETINET6 */
3350                 break;
3351
3352           case O_TRUSTUSER:
3353 # if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
3354                 if (!UseMSP)
3355                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3356                                              "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
3357 # endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
3358                 if (isascii(*val) && isdigit(*val))
3359                         TrustedUid = atoi(val);
3360                 else
3361                 {
3362                         register struct passwd *pw;
3363
3364                         TrustedUid = 0;
3365                         pw = sm_getpwnam(val);
3366                         if (pw == NULL)
3367                         {
3368                                 syserr("readcf: option TrustedUser: unknown user %s", val);
3369                                 break;
3370                         }
3371                         else
3372                                 TrustedUid = pw->pw_uid;
3373                 }
3374
3375 # ifdef UID_MAX
3376                 if (TrustedUid > UID_MAX)
3377                 {
3378                         syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
3379                                 (long) TrustedUid, (long) UID_MAX);
3380                         TrustedUid = 0;
3381                 }
3382 # endif /* UID_MAX */
3383                 break;
3384
3385           case O_MAXMIMEHDRLEN:
3386                 p = strchr(val, '/');
3387                 if (p != NULL)
3388                         *p++ = '\0';
3389                 MaxMimeHeaderLength = atoi(val);
3390                 if (p != NULL && *p != '\0')
3391                         MaxMimeFieldLength = atoi(p);
3392                 else
3393                         MaxMimeFieldLength = MaxMimeHeaderLength / 2;
3394
3395                 if (MaxMimeHeaderLength <= 0)
3396                         MaxMimeHeaderLength = 0;
3397                 else if (MaxMimeHeaderLength < 128)
3398                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3399                                              "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
3400
3401                 if (MaxMimeFieldLength <= 0)
3402                         MaxMimeFieldLength = 0;
3403                 else if (MaxMimeFieldLength < 40)
3404                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3405                                              "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
3406
3407                 /*
3408                 **  Headers field values now include leading space, so let's
3409                 **  adjust the values to be "backward compatible".
3410                 */
3411
3412                 if (MaxMimeHeaderLength > 0)
3413                         MaxMimeHeaderLength++;
3414                 if (MaxMimeFieldLength > 0)
3415                         MaxMimeFieldLength++;
3416                 break;
3417
3418           case O_CONTROLSOCKET:
3419                 PSTRSET(ControlSocketName, val);
3420                 break;
3421
3422           case O_MAXHDRSLEN:
3423                 MaxHeadersLength = atoi(val);
3424
3425                 if (MaxHeadersLength > 0 &&
3426                     MaxHeadersLength < (MAXHDRSLEN / 2))
3427                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3428                                              "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
3429                                              (MAXHDRSLEN / 2));
3430                 break;
3431
3432           case O_PROCTITLEPREFIX:
3433                 PSTRSET(ProcTitlePrefix, val);
3434                 break;
3435
3436 #if SASL
3437           case O_SASLINFO:
3438 # if _FFR_ALLOW_SASLINFO
3439                 /*
3440                 **  Allow users to select their own authinfo file
3441                 **  under certain circumstances, otherwise just ignore
3442                 **  the option.  If the option isn't ignored, several
3443                 **  commands don't work very well, e.g., mailq.
3444                 **  However, this is not a "perfect" solution.
3445                 **  If mail is queued, the authentication info
3446                 **  will not be used in subsequent delivery attempts.
3447                 **  If we really want to support this, then it has
3448                 **  to be stored in the queue file.
3449                 */
3450                 if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
3451                     RunAsUid != RealUid)
3452                         break;
3453 # endif /* _FFR_ALLOW_SASLINFO */
3454                 PSTRSET(SASLInfo, val);
3455                 break;
3456
3457           case O_SASLMECH:
3458                 if (AuthMechanisms != NULL)
3459                         sm_free(AuthMechanisms); /* XXX */
3460                 if (*val != '\0')
3461                         AuthMechanisms = newstr(val);
3462                 else
3463                         AuthMechanisms = NULL;
3464                 break;
3465
3466           case O_SASLREALM:
3467                 if (AuthRealm != NULL)
3468                         sm_free(AuthRealm);
3469                 if (*val != '\0')
3470                         AuthRealm = newstr(val);
3471                 else
3472                         AuthRealm = NULL;
3473                 break;
3474
3475           case O_SASLOPTS:
3476                 while (val != NULL && *val != '\0')
3477                 {
3478                         switch (*val)
3479                         {
3480                           case 'A':
3481                                 SASLOpts |= SASL_AUTH_AUTH;
3482                                 break;
3483
3484                           case 'a':
3485                                 SASLOpts |= SASL_SEC_NOACTIVE;
3486                                 break;
3487
3488                           case 'c':
3489                                 SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
3490                                 break;
3491
3492                           case 'd':
3493                                 SASLOpts |= SASL_SEC_NODICTIONARY;
3494                                 break;
3495
3496                           case 'f':
3497                                 SASLOpts |= SASL_SEC_FORWARD_SECRECY;
3498                                 break;
3499
3500 #  if SASL >= 20101
3501                           case 'm':
3502                                 SASLOpts |= SASL_SEC_MUTUAL_AUTH;
3503                                 break;
3504 #  endif /* SASL >= 20101 */
3505
3506                           case 'p':
3507                                 SASLOpts |= SASL_SEC_NOPLAINTEXT;
3508                                 break;
3509
3510                           case 'y':
3511                                 SASLOpts |= SASL_SEC_NOANONYMOUS;
3512                                 break;
3513
3514                           case ' ':     /* ignore */
3515                           case '\t':    /* ignore */
3516                           case ',':     /* ignore */
3517                                 break;
3518
3519                           default:
3520                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3521                                                      "Warning: Option: %s unknown parameter '%c'\n",
3522                                                      OPTNAME,
3523                                                      (isascii(*val) &&
3524                                                         isprint(*val))
3525                                                         ? *val : '?');
3526                                 break;
3527                         }
3528                         ++val;
3529                         val = strpbrk(val, ", \t");
3530                         if (val != NULL)
3531                                 ++val;
3532                 }
3533                 break;
3534
3535           case O_SASLBITS:
3536                 MaxSLBits = atoi(val);
3537                 break;
3538
3539 #else /* SASL */
3540           case O_SASLINFO:
3541           case O_SASLMECH:
3542           case O_SASLREALM:
3543           case O_SASLOPTS:
3544           case O_SASLBITS:
3545                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3546                                      "Warning: Option: %s requires SASL support (-DSASL)\n",
3547                                      OPTNAME);
3548                 break;
3549 #endif /* SASL */
3550
3551 #if STARTTLS
3552           case O_SRVCERTFILE:
3553                 SET_STRING_EXP(SrvCertFile);
3554           case O_SRVKEYFILE:
3555                 SET_STRING_EXP(SrvKeyFile);
3556           case O_CLTCERTFILE:
3557                 SET_STRING_EXP(CltCertFile);
3558           case O_CLTKEYFILE:
3559                 SET_STRING_EXP(CltKeyFile);
3560           case O_CACERTFILE:
3561                 SET_STRING_EXP(CACertFile);
3562           case O_CACERTPATH:
3563                 SET_STRING_EXP(CACertPath);
3564           case O_DHPARAMS:
3565                 SET_STRING_EXP(DHParams);
3566 # if _FFR_TLS_1
3567           case O_DHPARAMS5:
3568                 SET_STRING_EXP(DHParams5);
3569           case O_CIPHERLIST:
3570                 SET_STRING_EXP(CipherList);
3571 # endif /* _FFR_TLS_1 */
3572           case O_CRLFILE:
3573 # if OPENSSL_VERSION_NUMBER > 0x00907000L
3574                 SET_STRING_EXP(CRLFile);
3575 # else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3576                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3577                                      "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
3578                                      OPTNAME);
3579                 break;
3580 # endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3581
3582 # if _FFR_CRLPATH
3583           case O_CRLPATH:
3584 #  if OPENSSL_VERSION_NUMBER > 0x00907000L
3585                 SET_STRING_EXP(CRLPath);
3586 #  else /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3587                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3588                                      "Warning: Option: %s requires at least OpenSSL 0.9.7\n",
3589                                      OPTNAME);
3590                 break;
3591 #  endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */
3592 # endif /* _FFR_CRLPATH */
3593
3594         /*
3595         **  XXX How about options per daemon/client instead of globally?
3596         **  This doesn't work well for some options, e.g., no server cert,
3597         **  but fine for others.
3598         **
3599         **  XXX Some people may want different certs per server.
3600         **
3601         **  See also srvfeatures()
3602         */
3603
3604           case O_TLS_SRV_OPTS:
3605                 while (val != NULL && *val != '\0')
3606                 {
3607                         switch (*val)
3608                         {
3609                           case 'V':
3610                                 TLS_Srv_Opts |= TLS_I_NO_VRFY;
3611                                 break;
3612 # if _FFR_TLS_1
3613                         /*
3614                         **  Server without a cert? That works only if
3615                         **  AnonDH is enabled as cipher, which is not in the
3616                         **  default list. Hence the CipherList option must
3617                         **  be available. Moreover: which clients support this
3618                         **  besides sendmail with this setting?
3619                         */
3620
3621                           case 'C':
3622                                 TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
3623                                 break;
3624 # endif /* _FFR_TLS_1 */
3625                           case ' ':     /* ignore */
3626                           case '\t':    /* ignore */
3627                           case ',':     /* ignore */
3628                                 break;
3629                           default:
3630                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3631                                                      "Warning: Option: %s unknown parameter '%c'\n",
3632                                                      OPTNAME,
3633                                                      (isascii(*val) &&
3634                                                         isprint(*val))
3635                                                         ? *val : '?');
3636                                 break;
3637                         }
3638                         ++val;
3639                         val = strpbrk(val, ", \t");
3640                         if (val != NULL)
3641                                 ++val;
3642                 }
3643                 break;
3644
3645           case O_RANDFILE:
3646                 PSTRSET(RandFile, val);
3647                 break;
3648
3649 #else /* STARTTLS */
3650           case O_SRVCERTFILE:
3651           case O_SRVKEYFILE:
3652           case O_CLTCERTFILE:
3653           case O_CLTKEYFILE:
3654           case O_CACERTFILE:
3655           case O_CACERTPATH:
3656           case O_DHPARAMS:
3657 # if _FFR_TLS_1
3658           case O_DHPARAMS5:
3659           case O_CIPHERLIST:
3660 # endif /* _FFR_TLS_1 */
3661           case O_CRLFILE:
3662 # if _FFR_CRLPATH
3663           case O_CRLPATH:
3664 # endif /* _FFR_CRLPATH */
3665           case O_RANDFILE:
3666                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3667                                      "Warning: Option: %s requires TLS support\n",
3668                                      OPTNAME);
3669                 break;
3670
3671 #endif /* STARTTLS */
3672
3673           case O_CLIENTPORT:
3674                 setclientoptions(val);
3675                 break;
3676
3677           case O_DF_BUFSIZE:
3678                 DataFileBufferSize = atoi(val);
3679                 break;
3680
3681           case O_XF_BUFSIZE:
3682                 XscriptFileBufferSize = atoi(val);
3683                 break;
3684
3685           case O_LDAPDEFAULTSPEC:
3686 #if LDAPMAP
3687                 ldapmap_set_defaults(val);
3688 #else /* LDAPMAP */
3689                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3690                                      "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
3691                                      OPTNAME);
3692 #endif /* LDAPMAP */
3693                 break;
3694
3695           case O_INPUTMILTER:
3696 #if MILTER
3697                 InputFilterList = newstr(val);
3698 #else /* MILTER */
3699                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3700                                      "Warning: Option: %s requires Milter support (-DMILTER)\n",
3701                                      OPTNAME);
3702 #endif /* MILTER */
3703                 break;
3704
3705           case O_MILTER:
3706 #if MILTER
3707                 milter_set_option(subopt, val, sticky);
3708 #else /* MILTER */
3709                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3710                                      "Warning: Option: %s requires Milter support (-DMILTER)\n",
3711                                      OPTNAME);
3712 #endif /* MILTER */
3713                 break;
3714
3715           case O_QUEUE_FILE_MODE:       /* queue file mode */
3716                 QueueFileMode = atooct(val) & 0777;
3717                 break;
3718
3719           case O_DLVR_MIN:      /* deliver by minimum time */
3720                 DeliverByMin = convtime(val, 's');
3721                 break;
3722
3723           /* modifiers {daemon_flags} for direct submissions */
3724           case O_DIRECTSUBMODIFIERS:
3725                 {
3726                         BITMAP256 m;    /* ignored */
3727                         extern ENVELOPE BlankEnvelope;
3728
3729                         macdefine(&BlankEnvelope.e_macro, A_PERM,
3730                                   macid("{daemon_flags}"),
3731                                   getmodifiers(val, m));
3732                 }
3733                 break;
3734
3735           case O_FASTSPLIT:
3736                 FastSplit = atoi(val);
3737                 break;
3738
3739           case O_MBDB:
3740                 Mbdb = newstr(val);
3741                 break;
3742
3743           case O_MSQ:
3744                 UseMSP = atobool(val);
3745                 break;
3746
3747           case O_SOFTBOUNCE:
3748                 SoftBounce = atobool(val);
3749                 break;
3750
3751           case O_REJECTLOGINTERVAL:     /* time btwn log msgs while refusing */
3752                 RejectLogInterval = convtime(val, 'h');
3753                 break;
3754
3755           case O_REQUIRES_DIR_FSYNC:
3756 #if REQUIRES_DIR_FSYNC
3757                 RequiresDirfsync = atobool(val);
3758 #else /* REQUIRES_DIR_FSYNC */
3759                 /* silently ignored... required for cf file option */
3760 #endif /* REQUIRES_DIR_FSYNC */
3761                 break;
3762
3763           case O_CONNECTION_RATE_WINDOW_SIZE:
3764                 ConnectionRateWindowSize = convtime(val, 's');
3765                 break;
3766
3767           case O_FALLBACKSMARTHOST:     /* fallback smart host */
3768                 if (val[0] != '\0')
3769                         FallbackSmartHost = newstr(val);
3770                 break;
3771
3772           case O_HELONAME:
3773                 HeloName = newstr(val);
3774                 break;
3775
3776 #if _FFR_MEMSTAT
3777           case O_REFUSELOWMEM:
3778                 RefuseLowMem = atoi(val);
3779                 break;
3780           case O_QUEUELOWMEM:
3781                 QueueLowMem = atoi(val);
3782                 break;
3783           case O_MEMRESOURCE:
3784                 MemoryResource = newstr(val);
3785                 break;
3786 #endif /* _FFR_MEMSTAT */
3787
3788           case O_MAXNOOPCOMMANDS:
3789                 MaxNOOPCommands = atoi(val);
3790                 break;
3791
3792 #if _FFR_MSG_ACCEPT
3793           case O_MSG_ACCEPT:
3794                 MessageAccept = newstr(val);
3795                 break;
3796 #endif /* _FFR_MSG_ACCEPT */
3797
3798 #if _FFR_QUEUE_RUN_PARANOIA
3799           case O_CHK_Q_RUNNERS:
3800                 CheckQueueRunners = atoi(val);
3801                 break;
3802 #endif /* _FFR_QUEUE_RUN_PARANOIA */
3803
3804 #if _FFR_EIGHT_BIT_ADDR_OK
3805           case O_EIGHT_BIT_ADDR_OK:
3806                 EightBitAddrOK = atobool(val);
3807                 break;
3808 #endif /* _FFR_EIGHT_BIT_ADDR_OK */
3809
3810           default:
3811                 if (tTd(37, 1))
3812                 {
3813                         if (isascii(opt) && isprint(opt))
3814                                 sm_dprintf("Warning: option %c unknown\n", opt);
3815                         else
3816                                 sm_dprintf("Warning: option 0x%x unknown\n", opt);
3817                 }
3818                 break;
3819         }
3820
3821         /*
3822         **  Options with suboptions are responsible for taking care
3823         **  of sticky-ness (e.g., that a command line setting is kept
3824         **  when reading in the sendmail.cf file).  This has to be done
3825         **  when the suboptions are parsed since each suboption must be
3826         **  sticky, not the root option.
3827         */
3828
3829         if (sticky && !bitset(OI_SUBOPT, o->o_flags))
3830                 setbitn(opt, StickyOpt);
3831 }
3832 /*
3833 **  SETCLASS -- set a string into a class
3834 **
3835 **      Parameters:
3836 **              class -- the class to put the string in.
3837 **              str -- the string to enter
3838 **
3839 **      Returns:
3840 **              none.
3841 **
3842 **      Side Effects:
3843 **              puts the word into the symbol table.
3844 */
3845
3846 void
3847 setclass(class, str)
3848         int class;
3849         char *str;
3850 {
3851         register STAB *s;
3852
3853         if ((str[0] & 0377) == MATCHCLASS)
3854         {
3855                 int mid;
3856
3857                 str++;
3858                 mid = macid(str);
3859                 if (mid == 0)
3860                         return;
3861
3862                 if (tTd(37, 8))
3863                         sm_dprintf("setclass(%s, $=%s)\n",
3864                                    macname(class), macname(mid));
3865                 copy_class(mid, class);
3866         }
3867         else
3868         {
3869                 if (tTd(37, 8))
3870                         sm_dprintf("setclass(%s, %s)\n", macname(class), str);
3871
3872                 s = stab(str, ST_CLASS, ST_ENTER);
3873                 setbitn(bitidx(class), s->s_class);
3874         }
3875 }
3876 /*
3877 **  MAKEMAPENTRY -- create a map entry
3878 **
3879 **      Parameters:
3880 **              line -- the config file line
3881 **
3882 **      Returns:
3883 **              A pointer to the map that has been created.
3884 **              NULL if there was a syntax error.
3885 **
3886 **      Side Effects:
3887 **              Enters the map into the dictionary.
3888 */
3889
3890 MAP *
3891 makemapentry(line)
3892         char *line;
3893 {
3894         register char *p;
3895         char *mapname;
3896         char *classname;
3897         register STAB *s;
3898         STAB *class;
3899
3900         for (p = line; isascii(*p) && isspace(*p); p++)
3901                 continue;
3902         if (!(isascii(*p) && isalnum(*p)))
3903         {
3904                 syserr("readcf: config K line: no map name");
3905                 return NULL;
3906         }
3907
3908         mapname = p;
3909         while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
3910                 continue;
3911         if (*p != '\0')
3912                 *p++ = '\0';
3913         while (isascii(*p) && isspace(*p))
3914                 p++;
3915         if (!(isascii(*p) && isalnum(*p)))
3916         {
3917                 syserr("readcf: config K line, map %s: no map class", mapname);
3918                 return NULL;
3919         }
3920         classname = p;
3921         while (isascii(*++p) && isalnum(*p))
3922                 continue;
3923         if (*p != '\0')
3924                 *p++ = '\0';
3925         while (isascii(*p) && isspace(*p))
3926                 p++;
3927
3928         /* look up the class */
3929         class = stab(classname, ST_MAPCLASS, ST_FIND);
3930         if (class == NULL)
3931         {
3932                 syserr("readcf: map %s: class %s not available", mapname,
3933                         classname);
3934                 return NULL;
3935         }
3936
3937         /* enter the map */
3938         s = stab(mapname, ST_MAP, ST_ENTER);
3939         s->s_map.map_class = &class->s_mapclass;
3940         s->s_map.map_mname = newstr(mapname);
3941
3942         if (class->s_mapclass.map_parse(&s->s_map, p))
3943                 s->s_map.map_mflags |= MF_VALID;
3944
3945         if (tTd(37, 5))
3946         {
3947                 sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
3948                            s->s_map.map_mname, s->s_map.map_class->map_cname,
3949                            s->s_map.map_mflags, s->s_map.map_file);
3950                 sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
3951                            s->s_map.map_app, s->s_map.map_domain,
3952                            s->s_map.map_rebuild);
3953         }
3954         return &s->s_map;
3955 }
3956 /*
3957 **  STRTORWSET -- convert string to rewriting set number
3958 **
3959 **      Parameters:
3960 **              p -- the pointer to the string to decode.
3961 **              endp -- if set, store the trailing delimiter here.
3962 **              stabmode -- ST_ENTER to create this entry, ST_FIND if
3963 **                      it must already exist.
3964 **
3965 **      Returns:
3966 **              The appropriate ruleset number.
3967 **              -1 if it is not valid (error already printed)
3968 */
3969
3970 int
3971 strtorwset(p, endp, stabmode)
3972         char *p;
3973         char **endp;
3974         int stabmode;
3975 {
3976         int ruleset;
3977         static int nextruleset = MAXRWSETS;
3978
3979         while (isascii(*p) && isspace(*p))
3980                 p++;
3981         if (!isascii(*p))
3982         {
3983                 syserr("invalid ruleset name: \"%.20s\"", p);
3984                 return -1;
3985         }
3986         if (isdigit(*p))
3987         {
3988                 ruleset = strtol(p, endp, 10);
3989                 if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
3990                 {
3991                         syserr("bad ruleset %d (%d max)",
3992                                 ruleset, MAXRWSETS / 2);
3993                         ruleset = -1;
3994                 }
3995         }
3996         else
3997         {
3998                 STAB *s;
3999                 char delim;
4000                 char *q = NULL;
4001
4002                 q = p;
4003                 while (*p != '\0' && isascii(*p) &&
4004                        (isalnum(*p) || *p == '_'))
4005                         p++;
4006                 if (q == p || !(isascii(*q) && isalpha(*q)))
4007                 {
4008                         /* no valid characters */
4009                         syserr("invalid ruleset name: \"%.20s\"", q);
4010                         return -1;
4011                 }
4012                 while (isascii(*p) && isspace(*p))
4013                         *p++ = '\0';
4014                 delim = *p;
4015                 if (delim != '\0')
4016                         *p = '\0';
4017                 s = stab(q, ST_RULESET, stabmode);
4018                 if (delim != '\0')
4019                         *p = delim;
4020
4021                 if (s == NULL)
4022                         return -1;
4023
4024                 if (stabmode == ST_ENTER && delim == '=')
4025                 {
4026                         while (isascii(*++p) && isspace(*p))
4027                                 continue;
4028                         if (!(isascii(*p) && isdigit(*p)))
4029                         {
4030                                 syserr("bad ruleset definition \"%s\" (number required after `=')", q);
4031                                 ruleset = -1;
4032                         }
4033                         else
4034                         {
4035                                 ruleset = strtol(p, endp, 10);
4036                                 if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4037                                 {
4038                                         syserr("bad ruleset number %d in \"%s\" (%d max)",
4039                                                 ruleset, q, MAXRWSETS / 2);
4040                                         ruleset = -1;
4041                                 }
4042                         }
4043                 }
4044                 else
4045                 {
4046                         if (endp != NULL)
4047                                 *endp = p;
4048                         if (s->s_ruleset >= 0)
4049                                 ruleset = s->s_ruleset;
4050                         else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
4051                         {
4052                                 syserr("%s: too many named rulesets (%d max)",
4053                                         q, MAXRWSETS / 2);
4054                                 ruleset = -1;
4055                         }
4056                 }
4057                 if (s->s_ruleset >= 0 &&
4058                     ruleset >= 0 &&
4059                     ruleset != s->s_ruleset)
4060                 {
4061                         syserr("%s: ruleset changed value (old %d, new %d)",
4062                                 q, s->s_ruleset, ruleset);
4063                         ruleset = s->s_ruleset;
4064                 }
4065                 else if (ruleset >= 0)
4066                 {
4067                         s->s_ruleset = ruleset;
4068                 }
4069                 if (stabmode == ST_ENTER && ruleset >= 0)
4070                 {
4071                         char *h = NULL;
4072
4073                         if (RuleSetNames[ruleset] != NULL)
4074                                 sm_free(RuleSetNames[ruleset]); /* XXX */
4075                         if (delim != '\0' && (h = strchr(q, delim)) != NULL)
4076                                 *h = '\0';
4077                         RuleSetNames[ruleset] = newstr(q);
4078                         if (delim == '/' && h != NULL)
4079                                 *h = delim;     /* put back delim */
4080                 }
4081         }
4082         return ruleset;
4083 }
4084 /*
4085 **  SETTIMEOUT -- set an individual timeout
4086 **
4087 **      Parameters:
4088 **              name -- the name of the timeout.
4089 **              val -- the value of the timeout.
4090 **              sticky -- if set, don't let other setoptions override
4091 **                      this value.
4092 **
4093 **      Returns:
4094 **              none.
4095 */
4096
4097 /* set if Timeout sub-option is stuck */
4098 static BITMAP256        StickyTimeoutOpt;
4099
4100 static struct timeoutinfo
4101 {
4102         char            *to_name;       /* long name of timeout */
4103         unsigned char   to_code;        /* code for option */
4104 } TimeOutTab[] =
4105 {
4106 #define TO_INITIAL                      0x01
4107         { "initial",                    TO_INITIAL                      },
4108 #define TO_MAIL                         0x02
4109         { "mail",                       TO_MAIL                         },
4110 #define TO_RCPT                         0x03
4111         { "rcpt",                       TO_RCPT                         },
4112 #define TO_DATAINIT                     0x04
4113         { "datainit",                   TO_DATAINIT                     },
4114 #define TO_DATABLOCK                    0x05
4115         { "datablock",                  TO_DATABLOCK                    },
4116 #define TO_DATAFINAL                    0x06
4117         { "datafinal",                  TO_DATAFINAL                    },
4118 #define TO_COMMAND                      0x07
4119         { "command",                    TO_COMMAND                      },
4120 #define TO_RSET                         0x08
4121         { "rset",                       TO_RSET                         },
4122 #define TO_HELO                         0x09
4123         { "helo",                       TO_HELO                         },
4124 #define TO_QUIT                         0x0A
4125         { "quit",                       TO_QUIT                         },
4126 #define TO_MISC                         0x0B
4127         { "misc",                       TO_MISC                         },
4128 #define TO_IDENT                        0x0C
4129         { "ident",                      TO_IDENT                        },
4130 #define TO_FILEOPEN                     0x0D
4131         { "fileopen",                   TO_FILEOPEN                     },
4132 #define TO_CONNECT                      0x0E
4133         { "connect",                    TO_CONNECT                      },
4134 #define TO_ICONNECT                     0x0F
4135         { "iconnect",                   TO_ICONNECT                     },
4136 #define TO_QUEUEWARN                    0x10
4137         { "queuewarn",                  TO_QUEUEWARN                    },
4138         { "queuewarn.*",                TO_QUEUEWARN                    },
4139 #define TO_QUEUEWARN_NORMAL             0x11
4140         { "queuewarn.normal",           TO_QUEUEWARN_NORMAL             },
4141 #define TO_QUEUEWARN_URGENT             0x12
4142         { "queuewarn.urgent",           TO_QUEUEWARN_URGENT             },
4143 #define TO_QUEUEWARN_NON_URGENT         0x13
4144         { "queuewarn.non-urgent",       TO_QUEUEWARN_NON_URGENT         },
4145 #define TO_QUEUERETURN                  0x14
4146         { "queuereturn",                TO_QUEUERETURN                  },
4147         { "queuereturn.*",              TO_QUEUERETURN                  },
4148 #define TO_QUEUERETURN_NORMAL           0x15
4149         { "queuereturn.normal",         TO_QUEUERETURN_NORMAL           },
4150 #define TO_QUEUERETURN_URGENT           0x16
4151         { "queuereturn.urgent",         TO_QUEUERETURN_URGENT           },
4152 #define TO_QUEUERETURN_NON_URGENT       0x17
4153         { "queuereturn.non-urgent",     TO_QUEUERETURN_NON_URGENT       },
4154 #define TO_HOSTSTATUS                   0x18
4155         { "hoststatus",                 TO_HOSTSTATUS                   },
4156 #define TO_RESOLVER_RETRANS             0x19
4157         { "resolver.retrans",           TO_RESOLVER_RETRANS             },
4158 #define TO_RESOLVER_RETRANS_NORMAL      0x1A
4159         { "resolver.retrans.normal",    TO_RESOLVER_RETRANS_NORMAL      },
4160 #define TO_RESOLVER_RETRANS_FIRST       0x1B
4161         { "resolver.retrans.first",     TO_RESOLVER_RETRANS_FIRST       },
4162 #define TO_RESOLVER_RETRY               0x1C
4163         { "resolver.retry",             TO_RESOLVER_RETRY               },
4164 #define TO_RESOLVER_RETRY_NORMAL        0x1D
4165         { "resolver.retry.normal",      TO_RESOLVER_RETRY_NORMAL        },
4166 #define TO_RESOLVER_RETRY_FIRST         0x1E
4167         { "resolver.retry.first",       TO_RESOLVER_RETRY_FIRST         },
4168 #define TO_CONTROL                      0x1F
4169         { "control",                    TO_CONTROL                      },
4170 #define TO_LHLO                         0x20
4171         { "lhlo",                       TO_LHLO                         },
4172 #define TO_AUTH                         0x21
4173         { "auth",                       TO_AUTH                         },
4174 #define TO_STARTTLS                     0x22
4175         { "starttls",                   TO_STARTTLS                     },
4176 #define TO_ACONNECT                     0x23
4177         { "aconnect",                   TO_ACONNECT                     },
4178 #define TO_QUEUEWARN_DSN                0x24
4179         { "queuewarn.dsn",              TO_QUEUEWARN_DSN                },
4180 #define TO_QUEUERETURN_DSN              0x25
4181         { "queuereturn.dsn",            TO_QUEUERETURN_DSN              },
4182         { NULL,                         0                               },
4183 };
4184
4185
4186 static void
4187 settimeout(name, val, sticky)
4188         char *name;
4189         char *val;
4190         bool sticky;
4191 {
4192         register struct timeoutinfo *to;
4193         int i, addopts;
4194         time_t toval;
4195
4196         if (tTd(37, 2))
4197                 sm_dprintf("settimeout(%s = %s)", name, val);
4198
4199         for (to = TimeOutTab; to->to_name != NULL; to++)
4200         {
4201                 if (sm_strcasecmp(to->to_name, name) == 0)
4202                         break;
4203         }
4204
4205         if (to->to_name == NULL)
4206         {
4207                 errno = 0; /* avoid bogus error text */
4208                 syserr("settimeout: invalid timeout %s", name);
4209                 return;
4210         }
4211
4212         /*
4213         **  See if this option is preset for us.
4214         */
4215
4216         if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
4217         {
4218                 if (tTd(37, 2))
4219                         sm_dprintf(" (ignored)\n");
4220                 return;
4221         }
4222
4223         if (tTd(37, 2))
4224                 sm_dprintf("\n");
4225
4226         toval = convtime(val, 'm');
4227         addopts = 0;
4228
4229         switch (to->to_code)
4230         {
4231           case TO_INITIAL:
4232                 TimeOuts.to_initial = toval;
4233                 break;
4234
4235           case TO_MAIL:
4236                 TimeOuts.to_mail = toval;
4237                 break;
4238
4239           case TO_RCPT:
4240                 TimeOuts.to_rcpt = toval;
4241                 break;
4242
4243           case TO_DATAINIT:
4244                 TimeOuts.to_datainit = toval;
4245                 break;
4246
4247           case TO_DATABLOCK:
4248                 TimeOuts.to_datablock = toval;
4249                 break;
4250
4251           case TO_DATAFINAL:
4252                 TimeOuts.to_datafinal = toval;
4253                 break;
4254
4255           case TO_COMMAND:
4256                 TimeOuts.to_nextcommand = toval;
4257                 break;
4258
4259           case TO_RSET:
4260                 TimeOuts.to_rset = toval;
4261                 break;
4262
4263           case TO_HELO:
4264                 TimeOuts.to_helo = toval;
4265                 break;
4266
4267           case TO_QUIT:
4268                 TimeOuts.to_quit = toval;
4269                 break;
4270
4271           case TO_MISC:
4272                 TimeOuts.to_miscshort = toval;
4273                 break;
4274
4275           case TO_IDENT:
4276                 TimeOuts.to_ident = toval;
4277                 break;
4278
4279           case TO_FILEOPEN:
4280                 TimeOuts.to_fileopen = toval;
4281                 break;
4282
4283           case TO_CONNECT:
4284                 TimeOuts.to_connect = toval;
4285                 break;
4286
4287           case TO_ICONNECT:
4288                 TimeOuts.to_iconnect = toval;
4289                 break;
4290
4291           case TO_ACONNECT:
4292                 TimeOuts.to_aconnect = toval;
4293                 break;
4294
4295           case TO_QUEUEWARN:
4296                 toval = convtime(val, 'h');
4297                 TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4298                 TimeOuts.to_q_warning[TOC_URGENT] = toval;
4299                 TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4300                 TimeOuts.to_q_warning[TOC_DSN] = toval;
4301                 addopts = 2;
4302                 break;
4303
4304           case TO_QUEUEWARN_NORMAL:
4305                 toval = convtime(val, 'h');
4306                 TimeOuts.to_q_warning[TOC_NORMAL] = toval;
4307                 break;
4308
4309           case TO_QUEUEWARN_URGENT:
4310                 toval = convtime(val, 'h');
4311                 TimeOuts.to_q_warning[TOC_URGENT] = toval;
4312                 break;
4313
4314           case TO_QUEUEWARN_NON_URGENT:
4315                 toval = convtime(val, 'h');
4316                 TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
4317                 break;
4318
4319           case TO_QUEUEWARN_DSN:
4320                 toval = convtime(val, 'h');
4321                 TimeOuts.to_q_warning[TOC_DSN] = toval;
4322                 break;
4323
4324           case TO_QUEUERETURN:
4325                 toval = convtime(val, 'd');
4326                 TimeOuts.to_q_return[TOC_NORMAL] = toval;
4327                 TimeOuts.to_q_return[TOC_URGENT] = toval;
4328                 TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4329                 TimeOuts.to_q_return[TOC_DSN] = toval;
4330                 addopts = 2;
4331                 break;
4332
4333           case TO_QUEUERETURN_NORMAL:
4334                 toval = convtime(val, 'd');
4335                 TimeOuts.to_q_return[TOC_NORMAL] = toval;
4336                 break;
4337
4338           case TO_QUEUERETURN_URGENT:
4339                 toval = convtime(val, 'd');
4340                 TimeOuts.to_q_return[TOC_URGENT] = toval;
4341                 break;
4342
4343           case TO_QUEUERETURN_NON_URGENT:
4344                 toval = convtime(val, 'd');
4345                 TimeOuts.to_q_return[TOC_NONURGENT] = toval;
4346                 break;
4347
4348           case TO_QUEUERETURN_DSN:
4349                 toval = convtime(val, 'd');
4350                 TimeOuts.to_q_return[TOC_DSN] = toval;
4351                 break;
4352
4353           case TO_HOSTSTATUS:
4354                 MciInfoTimeout = toval;
4355                 break;
4356
4357           case TO_RESOLVER_RETRANS:
4358                 toval = convtime(val, 's');
4359                 TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
4360                 TimeOuts.res_retrans[RES_TO_FIRST] = toval;
4361                 TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
4362                 addopts = 2;
4363                 break;
4364
4365           case TO_RESOLVER_RETRY:
4366                 i = atoi(val);
4367                 TimeOuts.res_retry[RES_TO_DEFAULT] = i;
4368                 TimeOuts.res_retry[RES_TO_FIRST] = i;
4369                 TimeOuts.res_retry[RES_TO_NORMAL] = i;
4370                 addopts = 2;
4371                 break;
4372
4373           case TO_RESOLVER_RETRANS_NORMAL:
4374                 TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
4375                 break;
4376
4377           case TO_RESOLVER_RETRY_NORMAL:
4378                 TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
4379                 break;
4380
4381           case TO_RESOLVER_RETRANS_FIRST:
4382                 TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
4383                 break;
4384
4385           case TO_RESOLVER_RETRY_FIRST:
4386                 TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
4387                 break;
4388
4389           case TO_CONTROL:
4390                 TimeOuts.to_control = toval;
4391                 break;
4392
4393           case TO_LHLO:
4394                 TimeOuts.to_lhlo = toval;
4395                 break;
4396
4397 #if SASL
4398           case TO_AUTH:
4399                 TimeOuts.to_auth = toval;
4400                 break;
4401 #endif /* SASL */
4402
4403 #if STARTTLS
4404           case TO_STARTTLS:
4405                 TimeOuts.to_starttls = toval;
4406                 break;
4407 #endif /* STARTTLS */
4408
4409           default:
4410                 syserr("settimeout: invalid timeout %s", name);
4411                 break;
4412         }
4413
4414         if (sticky)
4415         {
4416                 for (i = 0; i <= addopts; i++)
4417                         setbitn(to->to_code + i, StickyTimeoutOpt);
4418         }
4419 }
4420 /*
4421 **  INITTIMEOUTS -- parse and set timeout values
4422 **
4423 **      Parameters:
4424 **              val -- a pointer to the values.  If NULL, do initial
4425 **                      settings.
4426 **              sticky -- if set, don't let other setoptions override
4427 **                      this suboption value.
4428 **
4429 **      Returns:
4430 **              none.
4431 **
4432 **      Side Effects:
4433 **              Initializes the TimeOuts structure
4434 */
4435
4436 void
4437 inittimeouts(val, sticky)
4438         register char *val;
4439         bool sticky;
4440 {
4441         register char *p;
4442
4443         if (tTd(37, 2))
4444                 sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
4445         if (val == NULL)
4446         {
4447                 TimeOuts.to_connect = (time_t) 0 SECONDS;
4448                 TimeOuts.to_aconnect = (time_t) 0 SECONDS;
4449                 TimeOuts.to_iconnect = (time_t) 0 SECONDS;
4450                 TimeOuts.to_initial = (time_t) 5 MINUTES;
4451                 TimeOuts.to_helo = (time_t) 5 MINUTES;
4452                 TimeOuts.to_mail = (time_t) 10 MINUTES;
4453                 TimeOuts.to_rcpt = (time_t) 1 HOUR;
4454                 TimeOuts.to_datainit = (time_t) 5 MINUTES;
4455                 TimeOuts.to_datablock = (time_t) 1 HOUR;
4456                 TimeOuts.to_datafinal = (time_t) 1 HOUR;
4457                 TimeOuts.to_rset = (time_t) 5 MINUTES;
4458                 TimeOuts.to_quit = (time_t) 2 MINUTES;
4459                 TimeOuts.to_nextcommand = (time_t) 1 HOUR;
4460                 TimeOuts.to_miscshort = (time_t) 2 MINUTES;
4461 #if IDENTPROTO
4462                 TimeOuts.to_ident = (time_t) 5 SECONDS;
4463 #else /* IDENTPROTO */
4464                 TimeOuts.to_ident = (time_t) 0 SECONDS;
4465 #endif /* IDENTPROTO */
4466                 TimeOuts.to_fileopen = (time_t) 60 SECONDS;
4467                 TimeOuts.to_control = (time_t) 2 MINUTES;
4468                 TimeOuts.to_lhlo = (time_t) 2 MINUTES;
4469 #if SASL
4470                 TimeOuts.to_auth = (time_t) 10 MINUTES;
4471 #endif /* SASL */
4472 #if STARTTLS
4473                 TimeOuts.to_starttls = (time_t) 1 HOUR;
4474 #endif /* STARTTLS */
4475                 if (tTd(37, 5))
4476                 {
4477                         sm_dprintf("Timeouts:\n");
4478                         sm_dprintf("  connect = %ld\n",
4479                                    (long) TimeOuts.to_connect);
4480                         sm_dprintf("  aconnect = %ld\n",
4481                                    (long) TimeOuts.to_aconnect);
4482                         sm_dprintf("  initial = %ld\n",
4483                                    (long) TimeOuts.to_initial);
4484                         sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
4485                         sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
4486                         sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
4487                         sm_dprintf("  datainit = %ld\n",
4488                                    (long) TimeOuts.to_datainit);
4489                         sm_dprintf("  datablock = %ld\n",
4490                                    (long) TimeOuts.to_datablock);
4491                         sm_dprintf("  datafinal = %ld\n",
4492                                    (long) TimeOuts.to_datafinal);
4493                         sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
4494                         sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
4495                         sm_dprintf("  nextcommand = %ld\n",
4496                                    (long) TimeOuts.to_nextcommand);
4497                         sm_dprintf("  miscshort = %ld\n",
4498                                    (long) TimeOuts.to_miscshort);
4499                         sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
4500                         sm_dprintf("  fileopen = %ld\n",
4501                                    (long) TimeOuts.to_fileopen);
4502                         sm_dprintf("  lhlo = %ld\n",
4503                                    (long) TimeOuts.to_lhlo);
4504                         sm_dprintf("  control = %ld\n",
4505                                    (long) TimeOuts.to_control);
4506                 }
4507                 return;
4508         }
4509
4510         for (;; val = p)
4511         {
4512                 while (isascii(*val) && isspace(*val))
4513                         val++;
4514                 if (*val == '\0')
4515                         break;
4516                 for (p = val; *p != '\0' && *p != ','; p++)
4517                         continue;
4518                 if (*p != '\0')
4519                         *p++ = '\0';
4520
4521                 if (isascii(*val) && isdigit(*val))
4522                 {
4523                         /* old syntax -- set everything */
4524                         TimeOuts.to_mail = convtime(val, 'm');
4525                         TimeOuts.to_rcpt = TimeOuts.to_mail;
4526                         TimeOuts.to_datainit = TimeOuts.to_mail;
4527                         TimeOuts.to_datablock = TimeOuts.to_mail;
4528                         TimeOuts.to_datafinal = TimeOuts.to_mail;
4529                         TimeOuts.to_nextcommand = TimeOuts.to_mail;
4530                         if (sticky)
4531                         {
4532                                 setbitn(TO_MAIL, StickyTimeoutOpt);
4533                                 setbitn(TO_RCPT, StickyTimeoutOpt);
4534                                 setbitn(TO_DATAINIT, StickyTimeoutOpt);
4535                                 setbitn(TO_DATABLOCK, StickyTimeoutOpt);
4536                                 setbitn(TO_DATAFINAL, StickyTimeoutOpt);
4537                                 setbitn(TO_COMMAND, StickyTimeoutOpt);
4538                         }
4539                         continue;
4540                 }
4541                 else
4542                 {
4543                         register char *q = strchr(val, ':');
4544
4545                         if (q == NULL && (q = strchr(val, '=')) == NULL)
4546                         {
4547                                 /* syntax error */
4548                                 continue;
4549                         }
4550                         *q++ = '\0';
4551                         settimeout(val, q, sticky);
4552                 }
4553         }
4554 }