]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/main.c
This commit was generated by cvs2svn to compensate for changes in r56893,
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / main.c
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12
13 #ifndef lint
14 static char copyright[] =
15 "@(#) Copyright (c) 1998 Sendmail, Inc.  All rights reserved.\n\
16      Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
17      Copyright (c) 1988, 1993\n\
18         The Regents of the University of California.  All rights reserved.\n";
19 #endif /* not lint */
20
21 #ifndef lint
22 static char sccsid[] = "@(#)main.c      8.322 (Berkeley) 12/18/1998";
23 #endif /* not lint */
24
25 #define _DEFINE
26
27 #include "sendmail.h"
28 #include <arpa/inet.h>
29 #include <grp.h>
30 #if NAMED_BIND
31 #include <resolv.h>
32 #endif
33
34 /*
35 **  SENDMAIL -- Post mail to a set of destinations.
36 **
37 **      This is the basic mail router.  All user mail programs should
38 **      call this routine to actually deliver mail.  Sendmail in
39 **      turn calls a bunch of mail servers that do the real work of
40 **      delivering the mail.
41 **
42 **      Sendmail is driven by settings read in from /etc/sendmail.cf
43 **      (read by readcf.c).
44 **
45 **      Usage:
46 **              /usr/lib/sendmail [flags] addr ...
47 **
48 **              See the associated documentation for details.
49 **
50 **      Author:
51 **              Eric Allman, UCB/INGRES (until 10/81).
52 **                           Britton-Lee, Inc., purveyors of fine
53 **                              database computers (11/81 - 10/88).
54 **                           International Computer Science Institute
55 **                              (11/88 - 9/89).
56 **                           UCB/Mammoth Project (10/89 - 7/95).
57 **                           InReference, Inc. (8/95 - 1/97).
58 **                           Sendmail, Inc. (1/98 - present).
59 **              The support of the my employers is gratefully acknowledged.
60 **                      Few of them (Britton-Lee in particular) have had
61 **                      anything to gain from my involvement in this project.
62 */
63
64
65 int             NextMailer;     /* "free" index into Mailer struct */
66 char            *FullName;      /* sender's full name */
67 ENVELOPE        BlankEnvelope;  /* a "blank" envelope */
68 ENVELOPE        MainEnvelope;   /* the envelope around the basic letter */
69 ADDRESS         NullAddress =   /* a null address */
70                 { "", "", NULL, "" };
71 char            *CommandLineArgs;       /* command line args for pid file */
72 bool            Warn_Q_option = FALSE;  /* warn about Q option use */
73 char            **SaveArgv;     /* argument vector for re-execing */
74 int             MissingFds = 0; /* bit map of fds missing on startup */
75
76 #ifdef NGROUPS_MAX
77 GIDSET_T        InitialGidSet[NGROUPS_MAX];
78 #endif
79
80 static void     obsolete __P((char **));
81 extern void     printmailer __P((MAILER *));
82 extern void     tTflag __P((char *));
83
84 #if DAEMON && !SMTP
85 ERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
86 #endif /* DAEMON && !SMTP */
87 #if SMTP && !QUEUE
88 ERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
89 #endif /* DAEMON && !SMTP */
90
91 #define MAXCONFIGLEVEL  8       /* highest config version level known */
92
93 int
94 main(argc, argv, envp)
95         int argc;
96         char **argv;
97         char **envp;
98 {
99         register char *p;
100         char **av;
101         extern char Version[];
102         char *ep, *from;
103         STAB *st;
104         register int i;
105         int j;
106         bool queuemode = FALSE;         /* process queue requests */
107         bool safecf = TRUE;
108         bool warn_C_flag = FALSE;
109         char warn_f_flag = '\0';
110         bool run_in_foreground = FALSE; /* -bD mode */
111         static bool reenter = FALSE;
112         struct passwd *pw;
113         struct hostent *hp;
114         char *nullserver = NULL;
115         bool forged;
116         char jbuf[MAXHOSTNAMELEN];      /* holds MyHostName */
117         static char rnamebuf[MAXNAME];  /* holds RealUserName */
118         char *emptyenviron[1];
119         QUEUE_CHAR *new;
120         extern int DtableSize;
121         extern int optind;
122         extern int opterr;
123         extern char *optarg;
124         extern char **environ;
125         extern time_t convtime __P((char *, char));
126         extern SIGFUNC_DECL intsig __P((int));
127         extern struct hostent *myhostname __P((char *, int));
128         extern char *getauthinfo __P((int, bool *));
129         extern char *getcfname __P((void));
130         extern SIGFUNC_DECL sigusr1 __P((int));
131         extern SIGFUNC_DECL sighup __P((int));
132         extern SIGFUNC_DECL quiesce __P((int));
133         extern void initmacros __P((ENVELOPE *));
134         extern void init_md __P((int, char **));
135         extern int getdtsize __P((void));
136         extern void tTsetup __P((u_char *, int, char *));
137         extern void setdefaults __P((ENVELOPE *));
138         extern void initsetproctitle __P((int, char **, char **));
139         extern void init_vendor_macros __P((ENVELOPE *));
140         extern void load_if_names __P((void));
141         extern void vendor_pre_defaults __P((ENVELOPE *));
142         extern void vendor_post_defaults __P((ENVELOPE *));
143         extern void readcf __P((char *, bool, ENVELOPE *));
144         extern void printqueue __P((void));
145         extern void sendtoargv __P((char **, ENVELOPE *));
146         extern void resetlimits __P((void));
147 #ifndef HASUNSETENV
148         extern void unsetenv __P((char *));
149 #endif  
150
151         /*
152         **  Check to see if we reentered.
153         **      This would normally happen if e_putheader or e_putbody
154         **      were NULL when invoked.
155         */
156
157         if (reenter)
158         {
159                 syserr("main: reentered!");
160                 abort();
161         }
162         reenter = TRUE;
163
164         /* avoid null pointer dereferences */
165         TermEscape.te_rv_on = TermEscape.te_rv_off = "";
166
167         /* do machine-dependent initializations */
168         init_md(argc, argv);
169
170         /* in 4.4BSD, the table can be huge; impose a reasonable limit */
171         DtableSize = getdtsize();
172         if (DtableSize > 256)
173                 DtableSize = 256;
174
175         /*
176         **  Be sure we have enough file descriptors.
177         **      But also be sure that 0, 1, & 2 are open.
178         */
179
180         fill_fd(STDIN_FILENO, NULL);
181         fill_fd(STDOUT_FILENO, NULL);
182         fill_fd(STDERR_FILENO, NULL);
183
184         i = DtableSize;
185         while (--i > 0)
186         {
187                 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
188                         (void) close(i);
189         }
190         errno = 0;
191
192 #if LOG
193 # ifdef LOG_MAIL
194         openlog("sendmail", LOG_PID, LOG_MAIL);
195 # else 
196         openlog("sendmail", LOG_PID);
197 # endif
198 #endif 
199
200         if (MissingFds != 0)
201         {
202                 char mbuf[MAXLINE];
203
204                 mbuf[0] = '\0';
205                 if (bitset(1 << STDIN_FILENO, MissingFds))
206                         strcat(mbuf, ", stdin");
207                 if (bitset(1 << STDOUT_FILENO, MissingFds))
208                         strcat(mbuf, ", stdout");
209                 if (bitset(1 << STDERR_FILENO, MissingFds))
210                         strcat(mbuf, ", stderr");
211                 syserr("File descriptors missing on startup: %s", &mbuf[2]);
212         }
213
214         /* reset status from syserr() calls for missing file descriptors */
215         Errors = 0;
216         ExitStat = EX_OK;
217
218 #if XDEBUG
219         checkfd012("after openlog");
220 #endif
221
222         tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
223
224 #ifdef NGROUPS_MAX
225         /* save initial group set for future checks */
226         i = getgroups(NGROUPS_MAX, InitialGidSet);
227         if (i == 0)
228                 InitialGidSet[0] = (GID_T) -1;
229         while (i < NGROUPS_MAX)
230                 InitialGidSet[i++] = InitialGidSet[0];
231 #endif
232
233         /* drop group id privileges (RunAsUser not yet set) */
234         (void) drop_privileges(FALSE);
235
236 #ifdef SIGUSR1
237         /* arrange to dump state on user-1 signal */
238         setsignal(SIGUSR1, sigusr1);
239 #endif
240
241         /* initialize for setproctitle */
242         initsetproctitle(argc, argv, envp);
243
244         /* Handle any non-getoptable constructions. */
245         obsolete(argv);
246
247         /*
248         **  Do a quick prescan of the argument list.
249         */
250
251 #if defined(__osf__) || defined(_AIX3)
252 # define OPTIONS        "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
253 #endif
254 #if defined(sony_news)
255 # define OPTIONS        "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
256 #endif
257 #ifndef OPTIONS
258 # define OPTIONS        "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
259 #endif
260         opterr = 0;
261         while ((j = getopt(argc, argv, OPTIONS)) != -1)
262         {
263                 switch (j)
264                 {
265                   case 'd':
266                         /* hack attack -- see if should use ANSI mode */
267                         if (strcmp(optarg, "ANSI") == 0)
268                         {
269                                 TermEscape.te_rv_on = "\033[7m";
270                                 TermEscape.te_rv_off = "\033[0m";
271                                 break;
272                         }
273                         tTflag(optarg);
274                         setbuf(stdout, (char *) NULL);
275                         break;
276                 }
277         }
278         opterr = 1;
279
280         /* set up the blank envelope */
281         BlankEnvelope.e_puthdr = putheader;
282         BlankEnvelope.e_putbody = putbody;
283         BlankEnvelope.e_xfp = NULL;
284         STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
285         CurEnv = &BlankEnvelope;
286         STRUCTCOPY(NullAddress, MainEnvelope.e_from);
287
288         /*
289         **  Set default values for variables.
290         **      These cannot be in initialized data space.
291         */
292
293         setdefaults(&BlankEnvelope);
294
295         RealUid = getuid();
296         RealGid = getgid();
297
298         pw = sm_getpwuid(RealUid);
299         if (pw != NULL)
300                 (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
301         else
302                 (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
303         RealUserName = rnamebuf;
304
305         if (tTd(0, 101))
306         {
307                 printf("Version %s\n", Version);
308                 finis(FALSE, EX_OK);
309         }
310
311         /*
312         **  if running non-setuid binary as non-root, pretend
313         **  we are the RunAsUid
314         */
315         if (RealUid != 0 && geteuid() == RealUid)
316         {
317                 if (tTd(47, 1))
318                         printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
319                                 (int)RealUid);
320                 RunAsUid = RealUid;
321         }
322         else if (geteuid() != 0)
323                 RunAsUid = geteuid();
324
325         if (RealUid != 0 && getegid() == RealGid)
326                 RunAsGid = RealGid;
327
328         if (tTd(47, 5))
329         {
330                 printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
331                         (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
332                 printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
333         }
334
335         /* save command line arguments */
336         i = 0;
337         for (av = argv; *av != NULL; )
338                 i += strlen(*av++) + 1;
339         SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
340         CommandLineArgs = xalloc(i);
341         p = CommandLineArgs;
342         for (av = argv, i = 0; *av != NULL; )
343         {
344                 SaveArgv[i++] = newstr(*av);
345                 if (av != argv)
346                         *p++ = ' ';
347                 strcpy(p, *av++);
348                 p += strlen(p);
349         }
350         SaveArgv[i] = NULL;
351
352         if (tTd(0, 1))
353         {
354                 int ll;
355                 extern char *CompileOptions[];
356
357                 printf("Version %s\n Compiled with:", Version);
358                 av = CompileOptions;
359                 ll = 7;
360                 while (*av != NULL)
361                 {
362                         if (ll + strlen(*av) > 63)
363                         {
364                                 putchar('\n');
365                                 ll = 0;
366                         }
367                         if (ll == 0)
368                         {
369                                 putchar('\t');
370                                 putchar('\t');
371                         }
372                         else
373                                 putchar(' ');
374                         printf("%s", *av);
375                         ll += strlen(*av++) + 1;
376                 }
377                 putchar('\n');
378         }
379         if (tTd(0, 10))
380         {
381                 int ll;
382                 extern char *OsCompileOptions[];
383
384                 printf("    OS Defines:");
385                 av = OsCompileOptions;
386                 ll = 7;
387                 while (*av != NULL)
388                 {
389                         if (ll + strlen(*av) > 63)
390                         {
391                                 putchar('\n');
392                                 ll = 0;
393                         }
394                         if (ll == 0)
395                         {
396                                 putchar('\t');
397                                 putchar('\t');
398                         }
399                         else
400                                 putchar(' ');
401                         printf("%s", *av);
402                         ll += strlen(*av++) + 1;
403                 }
404                 putchar('\n');
405 #ifdef _PATH_UNIX
406                 printf("Kernel symbols:\t%s\n", _PATH_UNIX);
407 #endif
408                 printf(" Def Conf file:\t%s\n", getcfname());
409                 printf("      Pid file:\t%s\n", PidFile);
410         }
411
412         InChannel = stdin;
413         OutChannel = stdout;
414
415         /* clear sendmail's environment */
416         ExternalEnviron = environ;
417         emptyenviron[0] = NULL;
418         environ = emptyenviron;
419
420         /*
421         **  restore any original TZ setting until TimeZoneSpec has been
422         **  determined - or early log messages may get bogus time stamps
423         */
424         if ((p = getextenv("TZ")) != NULL)
425         {
426                 char *tz;
427                 int tzlen;
428
429                 tzlen = strlen(p) + 4;
430                 tz = xalloc(tzlen);
431                 snprintf(tz, tzlen, "TZ=%s", p);
432                 putenv(tz);
433         }
434
435         /* prime the child environment */
436         setuserenv("AGENT", "sendmail");
437
438         if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
439                 (void) setsignal(SIGINT, intsig);
440         (void) setsignal(SIGTERM, intsig);
441         (void) setsignal(SIGPIPE, SIG_IGN);
442         OldUmask = umask(022);
443         OpMode = MD_DELIVER;
444         FullName = getextenv("NAME");
445
446         /*
447         **  Initialize name server if it is going to be used.
448         */
449
450 #if NAMED_BIND
451         if (!bitset(RES_INIT, _res.options))
452                 res_init();
453         if (tTd(8, 8))
454                 _res.options |= RES_DEBUG;
455         else
456                 _res.options &= ~RES_DEBUG;
457 # ifdef RES_NOALIASES
458         _res.options |= RES_NOALIASES;
459 # endif
460 #endif
461
462         errno = 0;
463         from = NULL;
464
465         /* initialize some macros, etc. */
466         initmacros(CurEnv);
467         init_vendor_macros(CurEnv);
468
469         /* version */
470         define('v', Version, CurEnv);
471
472         /* hostname */
473         hp = myhostname(jbuf, sizeof jbuf);
474         if (jbuf[0] != '\0')
475         {
476                 struct  utsname utsname;
477
478                 if (tTd(0, 4))
479                         printf("canonical name: %s\n", jbuf);
480                 define('w', newstr(jbuf), CurEnv);      /* must be new string */
481                 define('j', newstr(jbuf), CurEnv);
482                 setclass('w', jbuf);
483
484                 p = strchr(jbuf, '.');
485                 if (p != NULL)
486                 {
487                         if (p[1] != '\0')
488                         {
489                                 define('m', newstr(&p[1]), CurEnv);
490                         }
491                         while (p != NULL && strchr(&p[1], '.') != NULL)
492                         {
493                                 *p = '\0';
494                                 if (tTd(0, 4))
495                                         printf("\ta.k.a.: %s\n", jbuf);
496                                 setclass('w', jbuf);
497                                 *p++ = '.';
498                                 p = strchr(p, '.');
499                         }
500                 }
501
502                 if (uname(&utsname) >= 0)
503                         p = utsname.nodename;
504                 else
505                 {
506                         if (tTd(0, 22))
507                                 printf("uname failed (%s)\n", errstring(errno));
508                         makelower(jbuf);
509                         p = jbuf;
510                 }
511                 if (tTd(0, 4))
512                         printf(" UUCP nodename: %s\n", p);
513                 p = newstr(p);
514                 define('k', p, CurEnv);
515                 setclass('k', p);
516                 setclass('w', p);
517         }
518         if (hp != NULL)
519         {
520                 for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
521                 {
522                         if (tTd(0, 4))
523                                 printf("\ta.k.a.: %s\n", *av);
524                         setclass('w', *av);
525                 }
526 #if NETINET
527                 if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
528                 {
529                         for (i = 0; hp->h_addr_list[i] != NULL; i++)
530                         {
531                                 char ipbuf[103];
532
533                                 snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
534                                         inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
535                                 if (tTd(0, 4))
536                                         printf("\ta.k.a.: %s\n", ipbuf);
537                                 setclass('w', ipbuf);
538                         }
539                 }
540 #endif
541         }
542
543         /* current time */
544         define('b', arpadate((char *) NULL), CurEnv);
545
546         QueueLimitRecipient = (QUEUE_CHAR *) NULL;
547         QueueLimitSender = (QUEUE_CHAR *) NULL;
548         QueueLimitId = (QUEUE_CHAR *) NULL;
549
550         /*
551         **  Crack argv.
552         */
553
554         av = argv;
555         p = strrchr(*av, '/');
556         if (p++ == NULL)
557                 p = *av;
558         if (strcmp(p, "newaliases") == 0)
559                 OpMode = MD_INITALIAS;
560         else if (strcmp(p, "mailq") == 0)
561                 OpMode = MD_PRINT;
562         else if (strcmp(p, "smtpd") == 0)
563                 OpMode = MD_DAEMON;
564         else if (strcmp(p, "hoststat") == 0)
565                 OpMode = MD_HOSTSTAT;
566         else if (strcmp(p, "purgestat") == 0)
567                 OpMode = MD_PURGESTAT;
568
569         optind = 1;
570         while ((j = getopt(argc, argv, OPTIONS)) != -1)
571         {
572                 switch (j)
573                 {
574                   case 'b':     /* operations mode */
575                         switch (j = *optarg)
576                         {
577                           case MD_DAEMON:
578                           case MD_FGDAEMON:
579 # if !DAEMON
580                                 usrerr("Daemon mode not implemented");
581                                 ExitStat = EX_USAGE;
582                                 break;
583 # endif /* DAEMON */
584                           case MD_SMTP:
585 # if !SMTP
586                                 usrerr("I don't speak SMTP");
587                                 ExitStat = EX_USAGE;
588                                 break;
589 # endif /* SMTP */
590
591                           case MD_INITALIAS:
592                           case MD_DELIVER:
593                           case MD_VERIFY:
594                           case MD_TEST:
595                           case MD_PRINT:
596                           case MD_HOSTSTAT:
597                           case MD_PURGESTAT:
598                           case MD_ARPAFTP:
599                                 OpMode = j;
600                                 break;
601
602                           case MD_FREEZE:
603                                 usrerr("Frozen configurations unsupported");
604                                 ExitStat = EX_USAGE;
605                                 break;
606
607                           default:
608                                 usrerr("Invalid operation mode %c", j);
609                                 ExitStat = EX_USAGE;
610                                 break;
611                         }
612                         break;
613
614                   case 'B':     /* body type */
615                         CurEnv->e_bodytype = optarg;
616                         break;
617
618                   case 'C':     /* select configuration file (already done) */
619                         if (RealUid != 0)
620                                 warn_C_flag = TRUE;
621                         ConfFile = optarg;
622                         (void) drop_privileges(TRUE);
623                         safecf = FALSE;
624                         break;
625
626                   case 'd':     /* debugging -- already done */
627                         break;
628
629                   case 'f':     /* from address */
630                   case 'r':     /* obsolete -f flag */
631                         if (from != NULL)
632                         {
633                                 usrerr("More than one \"from\" person");
634                                 ExitStat = EX_USAGE;
635                                 break;
636                         }
637                         from = newstr(denlstring(optarg, TRUE, TRUE));
638                         if (strcmp(RealUserName, from) != 0)
639                                 warn_f_flag = j;
640                         break;
641
642                   case 'F':     /* set full name */
643                         FullName = newstr(optarg);
644                         break;
645
646                   case 'h':     /* hop count */
647                         CurEnv->e_hopcount = strtol(optarg, &ep, 10);
648                         if (*ep)
649                         {
650                                 usrerr("Bad hop count (%s)", optarg);
651                                 ExitStat = EX_USAGE;
652                         }
653                         break;
654                 
655                   case 'n':     /* don't alias */
656                         NoAlias = TRUE;
657                         break;
658
659                   case 'N':     /* delivery status notifications */
660                         DefaultNotify |= QHASNOTIFY;
661                         if (strcasecmp(optarg, "never") == 0)
662                                 break;
663                         for (p = optarg; p != NULL; optarg = p)
664                         {
665                                 p = strchr(p, ',');
666                                 if (p != NULL)
667                                         *p++ = '\0';
668                                 if (strcasecmp(optarg, "success") == 0)
669                                         DefaultNotify |= QPINGONSUCCESS;
670                                 else if (strcasecmp(optarg, "failure") == 0)
671                                         DefaultNotify |= QPINGONFAILURE;
672                                 else if (strcasecmp(optarg, "delay") == 0)
673                                         DefaultNotify |= QPINGONDELAY;
674                                 else
675                                 {
676                                         usrerr("Invalid -N argument");
677                                         ExitStat = EX_USAGE;
678                                 }
679                         }
680                         break;
681
682                   case 'o':     /* set option */
683                         setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
684                         break;
685
686                   case 'O':     /* set option (long form) */
687                         setoption(' ', optarg, FALSE, TRUE, CurEnv);
688                         break;
689
690                   case 'p':     /* set protocol */
691                         p = strchr(optarg, ':');
692                         if (p != NULL)
693                         {
694                                 *p++ = '\0';
695                                 if (*p != '\0')
696                                 {
697                                         ep = xalloc(strlen(p) + 1);
698                                         cleanstrcpy(ep, p, MAXNAME);
699                                         define('s', ep, CurEnv);
700                                 }
701                         }
702                         if (*optarg != '\0')
703                         {
704                                 ep = xalloc(strlen(optarg) + 1);
705                                 cleanstrcpy(ep, optarg, MAXNAME);
706                                 define('r', ep, CurEnv);
707                         }
708                         break;
709
710                   case 'q':     /* run queue files at intervals */
711 # if QUEUE
712                         FullName = NULL;
713                         queuemode = TRUE;
714                         switch (optarg[0])
715                         {
716                           case 'I':
717                                 if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
718                                         syserr("!Out of memory!!");
719                                 new->queue_match = newstr(&optarg[1]);
720                                 new->queue_next = QueueLimitId;
721                                 QueueLimitId = new;
722                                 break;
723
724                           case 'R':
725                                 if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
726                                         syserr("!Out of memory!!");
727                                 new->queue_match = newstr(&optarg[1]);
728                                 new->queue_next = QueueLimitRecipient;
729                                 QueueLimitRecipient = new;
730                                 break;
731
732                           case 'S':
733                                 if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
734                                         syserr("!Out of memory!!");
735                                 new->queue_match = newstr(&optarg[1]);
736                                 new->queue_next = QueueLimitSender;
737                                 QueueLimitSender = new;
738                                 break;
739
740                           default:
741                                 QueueIntvl = convtime(optarg, 'm');
742                                 break;
743                         }
744 # else /* QUEUE */
745                         usrerr("I don't know about queues");
746                         ExitStat = EX_USAGE;
747 # endif /* QUEUE */
748                         break;
749
750                   case 'R':     /* DSN RET: what to return */
751                         if (bitset(EF_RET_PARAM, CurEnv->e_flags))
752                         {
753                                 usrerr("Duplicate -R flag");
754                                 ExitStat = EX_USAGE;
755                                 break;
756                         }
757                         CurEnv->e_flags |= EF_RET_PARAM;
758                         if (strcasecmp(optarg, "hdrs") == 0)
759                                 CurEnv->e_flags |= EF_NO_BODY_RETN;
760                         else if (strcasecmp(optarg, "full") != 0)
761                         {
762                                 usrerr("Invalid -R value");
763                                 ExitStat = EX_USAGE;
764                         }
765                         break;
766
767                   case 't':     /* read recipients from message */
768                         GrabTo = TRUE;
769                         break;
770
771                   case 'U':     /* initial (user) submission */
772                         UserSubmission = TRUE;
773                         break;
774
775                   case 'V':     /* DSN ENVID: set "original" envelope id */
776                         if (!xtextok(optarg))
777                         {
778                                 usrerr("Invalid syntax in -V flag");
779                                 ExitStat = EX_USAGE;
780                         }
781                         else
782                                 CurEnv->e_envid = newstr(optarg);
783                         break;
784
785                   case 'X':     /* traffic log file */
786                         (void) drop_privileges(TRUE);
787                         TrafficLogFile = fopen(optarg, "a");
788                         if (TrafficLogFile == NULL)
789                         {
790                                 syserr("cannot open %s", optarg);
791                                 ExitStat = EX_CANTCREAT;
792                                 break;
793                         }
794 #ifdef HASSETVBUF
795                         setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
796 #else
797                         setlinebuf(TrafficLogFile);
798 #endif
799                         break;
800
801                         /* compatibility flags */
802                   case 'c':     /* connect to non-local mailers */
803                   case 'i':     /* don't let dot stop me */
804                   case 'm':     /* send to me too */
805                   case 'T':     /* set timeout interval */
806                   case 'v':     /* give blow-by-blow description */
807                         setoption(j, "T", FALSE, TRUE, CurEnv);
808                         break;
809
810                   case 'e':     /* error message disposition */
811                   case 'M':     /* define macro */
812                         setoption(j, optarg, FALSE, TRUE, CurEnv);
813                         break;
814
815                   case 's':     /* save From lines in headers */
816                         setoption('f', "T", FALSE, TRUE, CurEnv);
817                         break;
818
819 # ifdef DBM
820                   case 'I':     /* initialize alias DBM file */
821                         OpMode = MD_INITALIAS;
822                         break;
823 # endif /* DBM */
824
825 # if defined(__osf__) || defined(_AIX3)
826                   case 'x':     /* random flag that OSF/1 & AIX mailx passes */
827                         break;
828 # endif
829 # if defined(sony_news)
830                   case 'E':
831                   case 'J':     /* ignore flags for Japanese code conversion
832                                    impremented on Sony NEWS */
833                         break;
834 # endif
835
836                   default:
837                         finis(TRUE, EX_USAGE);
838                         break;
839                 }
840         }
841         av += optind;
842
843         /*
844         **  Do basic initialization.
845         **      Read system control file.
846         **      Extract special fields for local use.
847         */
848
849         /* set up ${opMode} for use in config file */
850         {
851                 char mbuf[2];
852
853                 mbuf[0] = OpMode;
854                 mbuf[1] = '\0';
855                 define(MID_OPMODE, newstr(mbuf), CurEnv);
856         }
857
858 #if XDEBUG
859         checkfd012("before readcf");
860 #endif
861         vendor_pre_defaults(CurEnv);
862         readcf(getcfname(), safecf, CurEnv);
863         ConfigFileRead = TRUE;
864         vendor_post_defaults(CurEnv);
865
866         /* Enforce use of local time (null string overrides this) */
867         if (TimeZoneSpec == NULL)
868                 unsetenv("TZ");
869         else if (TimeZoneSpec[0] != '\0')
870                 setuserenv("TZ", TimeZoneSpec);
871         else
872                 setuserenv("TZ", NULL);
873         tzset();
874
875         /* avoid denial-of-service attacks */
876         resetlimits();
877
878         if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
879         {
880                 /* drop privileges -- daemon mode done after socket/bind */
881                 (void) drop_privileges(FALSE);
882         }
883
884         /*
885         **  Find our real host name for future logging.
886         */
887
888         p = getauthinfo(STDIN_FILENO, &forged);
889         define('_', p, CurEnv);
890
891         /* suppress error printing if errors mailed back or whatever */
892         if (CurEnv->e_errormode != EM_PRINT)
893                 HoldErrs = TRUE;
894
895         /* set up the $=m class now, after .cf has a chance to redefine $m */
896         expand("\201m", jbuf, sizeof jbuf, CurEnv);
897         setclass('m', jbuf);
898
899         /* probe interfaces and locate any additional names */
900         if (!DontProbeInterfaces)
901                 load_if_names();
902
903         if (tTd(0, 1))
904         {
905                 printf("\n============ SYSTEM IDENTITY (after readcf) ============");
906                 printf("\n      (short domain name) $w = ");
907                 xputs(macvalue('w', CurEnv));
908                 printf("\n  (canonical domain name) $j = ");
909                 xputs(macvalue('j', CurEnv));
910                 printf("\n         (subdomain name) $m = ");
911                 xputs(macvalue('m', CurEnv));
912                 printf("\n              (node name) $k = ");
913                 xputs(macvalue('k', CurEnv));
914                 printf("\n========================================================\n\n");
915         }
916
917         /*
918         **  Do more command line checking -- these are things that
919         **  have to modify the results of reading the config file.
920         */
921
922         /* process authorization warnings from command line */
923         if (warn_C_flag)
924                 auth_warning(CurEnv, "Processed by %s with -C %s",
925                         RealUserName, ConfFile);
926         if (Warn_Q_option)
927                 auth_warning(CurEnv, "Processed from queue %s", QueueDir);
928
929         /* check body type for legality */
930         if (CurEnv->e_bodytype == NULL)
931                 /* nothing */ ;
932         else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
933                 SevenBitInput = TRUE;
934         else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
935                 SevenBitInput = FALSE;
936         else
937         {
938                 usrerr("Illegal body type %s", CurEnv->e_bodytype);
939                 CurEnv->e_bodytype = NULL;
940         }
941
942         /* tweak default DSN notifications */
943         if (DefaultNotify == 0)
944                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
945
946         /* be sure we don't pick up bogus HOSTALIASES environment variable */
947         if (queuemode && RealUid != 0)
948                 (void) unsetenv("HOSTALIASES");
949
950         /* check for sane configuration level */
951         if (ConfigLevel > MAXCONFIGLEVEL)
952         {
953                 syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
954                         ConfigLevel, Version, MAXCONFIGLEVEL);
955         }
956
957         /* need MCI cache to have persistence */
958         if (HostStatDir != NULL && MaxMciCache == 0)
959         {
960                 HostStatDir = NULL;
961                 printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
962         }
963
964         /* need HostStatusDir in order to have SingleThreadDelivery */
965         if (SingleThreadDelivery && HostStatDir == NULL)
966         {
967                 SingleThreadDelivery = FALSE;
968                 printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
969         }
970
971         /* check for permissions */
972         if ((OpMode == MD_DAEMON ||
973              OpMode == MD_FGDAEMON ||
974              OpMode == MD_PURGESTAT) &&
975             RealUid != 0 &&
976             RealUid != TrustedUid)
977         {
978                 if (LogLevel > 1)
979                         sm_syslog(LOG_ALERT, NOQID,
980                                 "user %d attempted to %s",
981                                 RealUid,
982                                 OpMode != MD_PURGESTAT ? "run daemon"
983                                                        : "purge host status");
984                 usrerr("Permission denied");
985                 finis(FALSE, EX_USAGE);
986         }
987         if (OpMode == MD_INITALIAS &&
988             RealUid != 0 &&
989             RealUid != TrustedUid &&
990             !wordinclass(RealUserName, 't'))
991         {
992                 if (LogLevel > 1)
993                         sm_syslog(LOG_ALERT, NOQID,
994                                   "user %d attempted to rebuild the alias map",
995                                   RealUid);
996                 usrerr("Permission denied");
997                 finis(FALSE, EX_USAGE);
998         }
999
1000         if (MeToo)
1001                 BlankEnvelope.e_flags |= EF_METOO;
1002
1003         switch (OpMode)
1004         {
1005           case MD_TEST:
1006                 /* don't have persistent host status in test mode */
1007                 HostStatDir = NULL;
1008                 if (Verbose == 0)
1009                         Verbose = 2;
1010                 CurEnv->e_errormode = EM_PRINT;
1011                 HoldErrs = FALSE;
1012                 break;
1013
1014           case MD_VERIFY:
1015                 CurEnv->e_errormode = EM_PRINT;
1016                 HoldErrs = FALSE;
1017                 /* arrange to exit cleanly on hangup signal */
1018                 if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1019                         setsignal(SIGHUP, intsig);
1020                 break;
1021
1022           case MD_FGDAEMON:
1023                 run_in_foreground = TRUE;
1024                 OpMode = MD_DAEMON;
1025                 /* fall through ... */
1026
1027           case MD_DAEMON:
1028                 vendor_daemon_setup(CurEnv);
1029
1030                 /* remove things that don't make sense in daemon mode */
1031                 FullName = NULL;
1032                 GrabTo = FALSE;
1033
1034                 /* arrange to restart on hangup signal */
1035                 if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1036                         sm_syslog(LOG_WARNING, NOQID,
1037                                 "daemon invoked without full pathname; kill -1 won't work");
1038                 setsignal(SIGHUP, sighup);
1039
1040                 /* workaround: can't seem to release the signal in the parent */
1041                 releasesignal(SIGHUP);
1042                 break;
1043
1044           case MD_INITALIAS:
1045                 Verbose = 2;
1046                 CurEnv->e_errormode = EM_PRINT;
1047                 HoldErrs = FALSE;
1048                 /* fall through... */
1049
1050           case MD_PRINT:
1051                 /* to handle sendmail -bp -qSfoobar properly */
1052                 queuemode = FALSE;
1053                 /* fall through... */
1054
1055           default:
1056                 /* arrange to exit cleanly on hangup signal */
1057                 if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1058                         setsignal(SIGHUP, intsig);
1059                 break;
1060         }
1061
1062         /* special considerations for FullName */
1063         if (FullName != NULL)
1064         {
1065                 char *full = NULL;
1066                 extern bool rfc822_string __P((char *));
1067
1068                 /* full names can't have newlines */
1069                 if (strchr(FullName, '\n') != NULL) 
1070                 {
1071                         FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
1072                 }
1073                 /* check for characters that may have to be quoted */
1074                 if (!rfc822_string(FullName))
1075                 {
1076                         extern char *addquotes __P((char *));
1077
1078                         /*
1079                         **  Quote a full name with special characters
1080                         **  as a comment so crackaddr() doesn't destroy
1081                         **  the name portion of the address.
1082                         */
1083                         FullName = addquotes(FullName);
1084                         if (full != NULL)
1085                                 free(full);
1086                 }
1087         }
1088
1089         /* do heuristic mode adjustment */
1090         if (Verbose)
1091         {
1092                 /* turn off noconnect option */
1093                 setoption('c', "F", TRUE, FALSE, CurEnv);
1094
1095                 /* turn on interactive delivery */
1096                 setoption('d', "", TRUE, FALSE, CurEnv);
1097         }
1098
1099 #ifdef VENDOR_CODE
1100         /* check for vendor mismatch */
1101         if (VendorCode != VENDOR_CODE)
1102         {
1103                 extern char *getvendor __P((int));
1104
1105                 message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1106                         getvendor(VENDOR_CODE), getvendor(VendorCode));
1107         }
1108 #endif
1109         
1110         /* check for out of date configuration level */
1111         if (ConfigLevel < MAXCONFIGLEVEL)
1112         {
1113                 message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1114                         Version, MAXCONFIGLEVEL, ConfigLevel);
1115         }
1116
1117         if (ConfigLevel < 3)
1118         {
1119                 UseErrorsTo = TRUE;
1120         }
1121
1122         /* set options that were previous macros */
1123         if (SmtpGreeting == NULL)
1124         {
1125                 if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1126                         SmtpGreeting = newstr(p);
1127                 else
1128                         SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1129         }
1130         if (UnixFromLine == NULL)
1131         {
1132                 if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1133                         UnixFromLine = newstr(p);
1134                 else
1135                         UnixFromLine = "From \201g  \201d";
1136         }
1137
1138         /* our name for SMTP codes */
1139         expand("\201j", jbuf, sizeof jbuf, CurEnv);
1140         MyHostName = jbuf;
1141         if (strchr(jbuf, '.') == NULL)
1142                 message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1143                         jbuf);
1144
1145         /* make certain that this name is part of the $=w class */
1146         setclass('w', MyHostName);
1147
1148         /* the indices of built-in mailers */
1149         st = stab("local", ST_MAILER, ST_FIND);
1150         if (st != NULL)
1151                 LocalMailer = st->s_mailer;
1152         else if (OpMode != MD_TEST || !warn_C_flag)
1153                 syserr("No local mailer defined");
1154
1155         st = stab("prog", ST_MAILER, ST_FIND);
1156         if (st == NULL)
1157                 syserr("No prog mailer defined");
1158         else
1159         {
1160                 ProgMailer = st->s_mailer;
1161                 clrbitn(M_MUSER, ProgMailer->m_flags);
1162         }
1163
1164         st = stab("*file*", ST_MAILER, ST_FIND);
1165         if (st == NULL)
1166                 syserr("No *file* mailer defined");
1167         else
1168         {
1169                 FileMailer = st->s_mailer;
1170                 clrbitn(M_MUSER, FileMailer->m_flags);
1171         }
1172
1173         st = stab("*include*", ST_MAILER, ST_FIND);
1174         if (st == NULL)
1175                 syserr("No *include* mailer defined");
1176         else
1177                 InclMailer = st->s_mailer;
1178
1179         if (ConfigLevel < 6)
1180         {
1181                 /* heuristic tweaking of local mailer for back compat */
1182                 if (LocalMailer != NULL)
1183                 {
1184                         setbitn(M_ALIASABLE, LocalMailer->m_flags);
1185                         setbitn(M_HASPWENT, LocalMailer->m_flags);
1186                         setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1187                         setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1188                         setbitn(M_CHECKPROG, LocalMailer->m_flags);
1189                         setbitn(M_CHECKFILE, LocalMailer->m_flags);
1190                         setbitn(M_CHECKUDB, LocalMailer->m_flags);
1191                 }
1192                 if (ProgMailer != NULL)
1193                         setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1194                 if (FileMailer != NULL)
1195                         setbitn(M_RUNASRCPT, FileMailer->m_flags);
1196         }
1197         if (ConfigLevel < 7)
1198         {
1199                 if (LocalMailer != NULL)
1200                         setbitn(M_VRFY250, LocalMailer->m_flags);
1201                 if (ProgMailer != NULL)
1202                         setbitn(M_VRFY250, ProgMailer->m_flags);
1203                 if (FileMailer != NULL)
1204                         setbitn(M_VRFY250, FileMailer->m_flags);
1205         }
1206
1207         /* MIME Content-Types that cannot be transfer encoded */
1208         setclass('n', "multipart/signed");
1209
1210         /* MIME message/xxx subtypes that can be treated as messages */
1211         setclass('s', "rfc822");
1212
1213         /* MIME Content-Transfer-Encodings that can be encoded */
1214         setclass('e', "7bit");
1215         setclass('e', "8bit");
1216         setclass('e', "binary");
1217
1218 #ifdef USE_B_CLASS
1219         /* MIME Content-Types that should be treated as binary */
1220         setclass('b', "image");
1221         setclass('b', "audio");
1222         setclass('b', "video");
1223         setclass('b', "application/octet-stream");
1224 #endif
1225
1226 #if _FFR_MAX_MIME_HEADER_LENGTH
1227         /* MIME headers which have fields to check for overflow */
1228         setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1229         setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1230
1231         /* MIME headers to check for length overflow */
1232         setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1233
1234         /* MIME headers to check for overflow and rebalance */
1235         setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1236         setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1237         setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1238         setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1239         setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1240 #endif
1241
1242         /* operate in queue directory */
1243         if (QueueDir == NULL)
1244         {
1245                 if (OpMode != MD_TEST)
1246                 {
1247                         syserr("QueueDirectory (Q) option must be set");
1248                         ExitStat = EX_CONFIG;
1249                 }
1250         }
1251         else
1252         {
1253                 /* test path to get warning messages */
1254                 (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
1255                 if (OpMode != MD_TEST && chdir(QueueDir) < 0)
1256                 {
1257                         syserr("cannot chdir(%s)", QueueDir);
1258                         ExitStat = EX_CONFIG;
1259                 }
1260         }
1261
1262         /* check host status directory for validity */
1263         if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1264         {
1265                 /* cannot use this value */
1266                 if (tTd(0, 2))
1267                         printf("Cannot use HostStatusDirectory = %s: %s\n",
1268                                 HostStatDir, errstring(errno));
1269                 HostStatDir = NULL;
1270         }
1271
1272 # if QUEUE
1273         if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1274         {
1275                 struct stat stbuf;
1276
1277                 /* check to see if we own the queue directory */
1278                 if (stat(".", &stbuf) < 0)
1279                         syserr("main: cannot stat %s", QueueDir);
1280                 if (stbuf.st_uid != RealUid)
1281                 {
1282                         /* nope, really a botch */
1283                         usrerr("You do not have permission to process the queue");
1284                         finis(FALSE, EX_NOPERM);
1285                 }
1286         }
1287 # endif /* QUEUE */
1288
1289         /* if we've had errors so far, exit now */
1290         if (ExitStat != EX_OK && OpMode != MD_TEST)
1291                 finis(FALSE, ExitStat);
1292
1293 #if XDEBUG
1294         checkfd012("before main() initmaps");
1295 #endif
1296
1297         /*
1298         **  Do operation-mode-dependent initialization.
1299         */
1300
1301         switch (OpMode)
1302         {
1303           case MD_PRINT:
1304                 /* print the queue */
1305 #if QUEUE
1306                 dropenvelope(CurEnv, TRUE);
1307                 signal(SIGPIPE, quiesce);
1308                 printqueue();
1309                 finis(FALSE, EX_OK);
1310 #else /* QUEUE */
1311                 usrerr("No queue to print");
1312                 finis(FALSE, ExitStat);
1313 #endif /* QUEUE */
1314                 break;
1315
1316           case MD_HOSTSTAT:
1317                 signal(SIGPIPE, quiesce);
1318                 mci_traverse_persistent(mci_print_persistent, NULL);
1319                 finis(FALSE, EX_OK);
1320                 break;
1321
1322           case MD_PURGESTAT:
1323                 mci_traverse_persistent(mci_purge_persistent, NULL);
1324                 finis(FALSE, EX_OK);
1325                 break;
1326
1327           case MD_INITALIAS:
1328                 /* initialize maps */
1329                 initmaps(TRUE, CurEnv);
1330                 finis(FALSE, ExitStat);
1331                 break;
1332
1333           case MD_SMTP:
1334           case MD_DAEMON:
1335                 /* reset DSN parameters */
1336                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1337                 CurEnv->e_envid = NULL;
1338                 CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1339
1340                 /* don't open maps for daemon -- done below in child */
1341                 break;
1342
1343           default:
1344                 /* open the maps */
1345                 initmaps(FALSE, CurEnv);
1346                 break;
1347         }
1348
1349         if (tTd(0, 15))
1350         {
1351                 extern void printrules __P((void));
1352
1353                 /* print configuration table (or at least part of it) */
1354                 if (tTd(0, 90))
1355                         printrules();
1356                 for (i = 0; i < MAXMAILERS; i++)
1357                 {
1358                         if (Mailer[i] != NULL)
1359                                 printmailer(Mailer[i]);
1360                 }
1361         }
1362
1363         /*
1364         **  Switch to the main envelope.
1365         */
1366
1367         CurEnv = newenvelope(&MainEnvelope, CurEnv);
1368         MainEnvelope.e_flags = BlankEnvelope.e_flags;
1369
1370         /*
1371         **  If test mode, read addresses from stdin and process.
1372         */
1373
1374         if (OpMode == MD_TEST)
1375         {
1376                 char buf[MAXLINE];
1377                 SIGFUNC_DECL intindebug __P((int));
1378
1379                 if (isatty(fileno(stdin)))
1380                         Verbose = 2;
1381
1382                 if (Verbose)
1383                 {
1384                         printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1385                         printf("Enter <ruleset> <address>\n");
1386                 }
1387                 if (setjmp(TopFrame) > 0)
1388                         printf("\n");
1389                 (void) setsignal(SIGINT, intindebug);
1390                 for (;;)
1391                 {
1392                         extern void testmodeline __P((char *, ENVELOPE *));
1393
1394                         if (Verbose == 2)
1395                                 printf("> ");
1396                         (void) fflush(stdout);
1397                         if (fgets(buf, sizeof buf, stdin) == NULL)
1398                                 finis(TRUE, ExitStat);
1399                         p = strchr(buf, '\n');
1400                         if (p != NULL)
1401                                 *p = '\0';
1402                         if (Verbose < 2)
1403                                 printf("> %s\n", buf);
1404                         testmodeline(buf, CurEnv);
1405                 }
1406         }
1407
1408 # if QUEUE
1409         /*
1410         **  If collecting stuff from the queue, go start doing that.
1411         */
1412
1413         if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1414         {
1415                 (void) runqueue(FALSE, Verbose);
1416                 finis(TRUE, ExitStat);
1417         }
1418 # endif /* QUEUE */
1419
1420         /*
1421         **  If a daemon, wait for a request.
1422         **      getrequests will always return in a child.
1423         **      If we should also be processing the queue, start
1424         **              doing it in background.
1425         **      We check for any errors that might have happened
1426         **              during startup.
1427         */
1428
1429         if (OpMode == MD_DAEMON || QueueIntvl != 0)
1430         {
1431                 char dtype[200];
1432                 extern void getrequests __P((ENVELOPE *));
1433
1434                 if (!run_in_foreground && !tTd(99, 100))
1435                 {
1436                         /* put us in background */
1437                         i = fork();
1438                         if (i < 0)
1439                                 syserr("daemon: cannot fork");
1440                         if (i != 0)
1441                                 finis(FALSE, EX_OK);
1442
1443                         /* disconnect from our controlling tty */
1444                         disconnect(2, CurEnv);
1445                 }
1446
1447                 dtype[0] = '\0';
1448                 if (OpMode == MD_DAEMON)
1449                         strcat(dtype, "+SMTP");
1450                 if (QueueIntvl != 0)
1451                 {
1452                         strcat(dtype, "+queueing@");
1453                         strcat(dtype, pintvl(QueueIntvl, TRUE));
1454                 }
1455                 if (tTd(0, 1))
1456                         strcat(dtype, "+debugging");
1457
1458                 sm_syslog(LOG_INFO, NOQID,
1459                         "starting daemon (%s): %s", Version, dtype + 1);
1460 #ifdef XLA
1461                 xla_create_file();
1462 #endif
1463
1464 # if QUEUE
1465                 if (queuemode)
1466                 {
1467                         (void) runqueue(TRUE, FALSE);
1468                         if (OpMode != MD_DAEMON)
1469                         {
1470                                 for (;;)
1471                                 {
1472                                         pause();
1473                                         if (DoQueueRun)
1474                                                 (void) runqueue(TRUE, FALSE);
1475                                 }
1476                         }
1477                 }
1478 # endif /* QUEUE */
1479                 dropenvelope(CurEnv, TRUE);
1480
1481 #if DAEMON
1482                 getrequests(CurEnv);
1483
1484                 /* drop privileges */
1485                 (void) drop_privileges(FALSE);
1486
1487                 /* at this point we are in a child: reset state */
1488                 (void) newenvelope(CurEnv, CurEnv);
1489
1490                 /*
1491                 **  Get authentication data
1492                 */
1493
1494                 p = getauthinfo(fileno(InChannel), &forged);
1495                 define('_', p, &BlankEnvelope);
1496 #endif /* DAEMON */
1497         }
1498
1499 # if SMTP
1500         /*
1501         **  If running SMTP protocol, start collecting and executing
1502         **  commands.  This will never return.
1503         */
1504
1505         if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1506         {
1507                 char pbuf[20];
1508                 extern void smtp __P((char *, ENVELOPE *));
1509
1510                 /*
1511                 **  Save some macros for check_* rulesets.
1512                 */
1513
1514                 if (forged)
1515                 {
1516                         char ipbuf[103];
1517
1518                         snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1519                                  inet_ntoa(RealHostAddr.sin.sin_addr));
1520
1521                         define(macid("{client_name}", NULL),
1522                                newstr(ipbuf), &BlankEnvelope);
1523                 }
1524                 else
1525                         define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
1526                 define(macid("{client_addr}", NULL),
1527                        newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1528                 if (RealHostAddr.sa.sa_family == AF_INET)
1529                         snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
1530                 else
1531                         snprintf(pbuf, sizeof pbuf, "0");
1532                 define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
1533
1534                 /* initialize maps now for check_relay ruleset */
1535                 initmaps(FALSE, CurEnv);
1536
1537                 if (OpMode == MD_DAEMON)
1538                 {
1539                         /* validate the connection */
1540                         HoldErrs = TRUE;
1541                         nullserver = validate_connection(&RealHostAddr,
1542                                                          RealHostName, CurEnv);
1543                         HoldErrs = FALSE;
1544                 }
1545                 smtp(nullserver, CurEnv);
1546         }
1547 # endif /* SMTP */
1548
1549         clearenvelope(CurEnv, FALSE);
1550         if (OpMode == MD_VERIFY)
1551         {
1552                 CurEnv->e_sendmode = SM_VERIFY;
1553                 PostMasterCopy = NULL;
1554         }
1555         else
1556         {
1557                 /* interactive -- all errors are global */
1558                 CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1559         }
1560
1561         /*
1562         **  Do basic system initialization and set the sender
1563         */
1564
1565         initsys(CurEnv);
1566         if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
1567                 auth_warning(CurEnv, "%s set sender to %s using -%c",
1568                         RealUserName, from, warn_f_flag);
1569         setsender(from, CurEnv, NULL, '\0', FALSE);
1570         if (macvalue('s', CurEnv) == NULL)
1571                 define('s', RealHostName, CurEnv);
1572
1573         if (*av == NULL && !GrabTo)
1574         {
1575                 CurEnv->e_flags |= EF_GLOBALERRS;
1576                 usrerr("Recipient names must be specified");
1577
1578                 /* collect body for UUCP return */
1579                 if (OpMode != MD_VERIFY)
1580                         collect(InChannel, FALSE, NULL, CurEnv);
1581                 finis(TRUE, ExitStat);
1582         }
1583
1584         /*
1585         **  Scan argv and deliver the message to everyone.
1586         */
1587
1588         sendtoargv(av, CurEnv);
1589
1590         /* if we have had errors sofar, arrange a meaningful exit stat */
1591         if (Errors > 0 && ExitStat == EX_OK)
1592                 ExitStat = EX_USAGE;
1593
1594 #if _FFR_FIX_DASHT
1595         /*
1596         **  If using -t, force not sending to argv recipients, even
1597         **  if they are mentioned in the headers.
1598         */
1599
1600         if (GrabTo)
1601         {
1602                 ADDRESS *q;
1603                 
1604                 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1605                         q->q_flags |= QDONTSEND;
1606         }
1607 #endif
1608
1609         /*
1610         **  Read the input mail.
1611         */
1612
1613         CurEnv->e_to = NULL;
1614         if (OpMode != MD_VERIFY || GrabTo)
1615         {
1616                 long savedflags = CurEnv->e_flags & EF_FATALERRS;
1617
1618                 CurEnv->e_flags |= EF_GLOBALERRS;
1619                 CurEnv->e_flags &= ~EF_FATALERRS;
1620                 collect(InChannel, FALSE, NULL, CurEnv);
1621
1622                 /* bail out if message too large */
1623                 if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
1624                 {
1625                         finis(TRUE, ExitStat);
1626                         /*NOTREACHED*/
1627                         return -1;
1628                 }
1629                 CurEnv->e_flags |= savedflags;
1630         }
1631         errno = 0;
1632
1633         if (tTd(1, 1))
1634                 printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1635
1636         /*
1637         **  Actually send everything.
1638         **      If verifying, just ack.
1639         */
1640
1641         CurEnv->e_from.q_flags |= QDONTSEND;
1642         if (tTd(1, 5))
1643         {
1644                 printf("main: QDONTSEND ");
1645                 printaddr(&CurEnv->e_from, FALSE);
1646         }
1647         CurEnv->e_to = NULL;
1648         CurrentLA = getla();
1649         GrabTo = FALSE;
1650         sendall(CurEnv, SM_DEFAULT);
1651
1652         /*
1653         **  All done.
1654         **      Don't send return error message if in VERIFY mode.
1655         */
1656
1657         finis(TRUE, ExitStat);
1658         /*NOTREACHED*/
1659         return -1;
1660 }
1661
1662 /* ARGSUSED */
1663 SIGFUNC_DECL
1664 quiesce(sig)
1665         int sig;
1666 {
1667         finis(FALSE, EX_OK);
1668 }
1669
1670 /* ARGSUSED */
1671 SIGFUNC_DECL
1672 intindebug(sig)
1673         int sig;
1674 {
1675         longjmp(TopFrame, 1);
1676         return SIGFUNC_RETURN;
1677 }
1678
1679
1680 \f/*
1681 **  FINIS -- Clean up and exit.
1682 **
1683 **      Parameters:
1684 **              drop -- whether or not to drop CurEnv envelope
1685 **              exitstat -- exit status to use for exit() call
1686 **
1687 **      Returns:
1688 **              never
1689 **
1690 **      Side Effects:
1691 **              exits sendmail
1692 */
1693
1694 void
1695 finis(drop, exitstat)
1696         bool drop;
1697         volatile int exitstat;
1698 {
1699         extern void closemaps __P((void));
1700 #ifdef USERDB
1701         extern void _udbx_close __P((void));
1702 #endif
1703
1704         if (tTd(2, 1))
1705         {
1706                 extern void printenvflags __P((ENVELOPE *));
1707
1708                 printf("\n====finis: stat %d e_id=%s e_flags=",
1709                         exitstat,
1710                         CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1711                 printenvflags(CurEnv);
1712         }
1713         if (tTd(2, 9))
1714                 printopenfds(FALSE);
1715
1716         /* if we fail in finis(), just exit */
1717         if (setjmp(TopFrame) != 0)
1718         {
1719                 /* failed -- just give it up */
1720                 goto forceexit;
1721         }
1722
1723         /* clean up temp files */
1724         CurEnv->e_to = NULL;
1725         if (drop && CurEnv->e_id != NULL)
1726                 dropenvelope(CurEnv, TRUE);
1727
1728         /* flush any cached connections */
1729         mci_flush(TRUE, NULL);
1730
1731         /* close maps belonging to this pid */
1732         closemaps();
1733
1734 #ifdef USERDB
1735         /* close UserDatabase */
1736         _udbx_close();
1737 #endif
1738
1739 # ifdef XLA
1740         /* clean up extended load average stuff */
1741         xla_all_end();
1742 # endif
1743
1744         /* and exit */
1745   forceexit:
1746         if (LogLevel > 78)
1747                 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1748                         "finis, pid=%d",
1749                         getpid());
1750         if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1751                 exitstat = EX_OK;
1752
1753         /* reset uid for process accounting */
1754         endpwent();
1755         setuid(RealUid);
1756
1757         exit(exitstat);
1758 }
1759 \f/*
1760 **  INTSIG -- clean up on interrupt
1761 **
1762 **      This just arranges to exit.  It pessimises in that it
1763 **      may resend a message.
1764 **
1765 **      Parameters:
1766 **              none.
1767 **
1768 **      Returns:
1769 **              none.
1770 **
1771 **      Side Effects:
1772 **              Unlocks the current job.
1773 */
1774
1775 /* ARGSUSED */
1776 SIGFUNC_DECL
1777 intsig(sig)
1778         int sig;
1779 {
1780         if (LogLevel > 79)
1781                 sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
1782         FileName = NULL;
1783         unlockqueue(CurEnv);
1784         closecontrolsocket(TRUE);
1785 #ifdef XLA
1786         xla_all_end();
1787 #endif
1788         finis(FALSE, EX_OK);
1789 }
1790 \f/*
1791 **  INITMACROS -- initialize the macro system
1792 **
1793 **      This just involves defining some macros that are actually
1794 **      used internally as metasymbols to be themselves.
1795 **
1796 **      Parameters:
1797 **              none.
1798 **
1799 **      Returns:
1800 **              none.
1801 **
1802 **      Side Effects:
1803 **              initializes several macros to be themselves.
1804 */
1805
1806 struct metamac  MetaMacros[] =
1807 {
1808         /* LHS pattern matching characters */
1809         { '*', MATCHZANY },     { '+', MATCHANY },      { '-', MATCHONE },
1810         { '=', MATCHCLASS },    { '~', MATCHNCLASS },
1811
1812         /* these are RHS metasymbols */
1813         { '#', CANONNET },      { '@', CANONHOST },     { ':', CANONUSER },
1814         { '>', CALLSUBR },
1815
1816         /* the conditional operations */
1817         { '?', CONDIF },        { '|', CONDELSE },      { '.', CONDFI },
1818
1819         /* the hostname lookup characters */
1820         { '[', HOSTBEGIN },     { ']', HOSTEND },
1821         { '(', LOOKUPBEGIN },   { ')', LOOKUPEND },
1822
1823         /* miscellaneous control characters */
1824         { '&', MACRODEXPAND },
1825
1826         { '\0' }
1827 };
1828
1829 #define MACBINDING(name, mid) \
1830                 stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
1831                 MacroName[mid] = name;
1832
1833 void
1834 initmacros(e)
1835         register ENVELOPE *e;
1836 {
1837         register struct metamac *m;
1838         register int c;
1839         char buf[5];
1840         extern char *MacroName[256];
1841
1842         for (m = MetaMacros; m->metaname != '\0'; m++)
1843         {
1844                 buf[0] = m->metaval;
1845                 buf[1] = '\0';
1846                 define(m->metaname, newstr(buf), e);
1847         }
1848         buf[0] = MATCHREPL;
1849         buf[2] = '\0';
1850         for (c = '0'; c <= '9'; c++)
1851         {
1852                 buf[1] = c;
1853                 define(c, newstr(buf), e);
1854         }
1855
1856         /* set defaults for some macros sendmail will use later */
1857         define('n', "MAILER-DAEMON", e);
1858
1859         /* set up external names for some internal macros */
1860         MACBINDING("opMode", MID_OPMODE);
1861         /*XXX should probably add equivalents for all short macros here XXX*/
1862 }
1863 \f/*
1864 **  DISCONNECT -- remove our connection with any foreground process
1865 **
1866 **      Parameters:
1867 **              droplev -- how "deeply" we should drop the line.
1868 **                      0 -- ignore signals, mail back errors, make sure
1869 **                           output goes to stdout.
1870 **                      1 -- also, make stdout go to transcript.
1871 **                      2 -- also, disconnect from controlling terminal
1872 **                           (only for daemon mode).
1873 **              e -- the current envelope.
1874 **
1875 **      Returns:
1876 **              none
1877 **
1878 **      Side Effects:
1879 **              Trys to insure that we are immune to vagaries of
1880 **              the controlling tty.
1881 */
1882
1883 void
1884 disconnect(droplev, e)
1885         int droplev;
1886         register ENVELOPE *e;
1887 {
1888         int fd;
1889
1890         if (tTd(52, 1))
1891                 printf("disconnect: In %d Out %d, e=%lx\n",
1892                         fileno(InChannel), fileno(OutChannel), (u_long) e);
1893         if (tTd(52, 100))
1894         {
1895                 printf("don't\n");
1896                 return;
1897         }
1898         if (LogLevel > 93)
1899                 sm_syslog(LOG_DEBUG, e->e_id,
1900                         "disconnect level %d",
1901                         droplev);
1902
1903         /* be sure we don't get nasty signals */
1904         (void) setsignal(SIGINT, SIG_IGN);
1905         (void) setsignal(SIGQUIT, SIG_IGN);
1906
1907         /* we can't communicate with our caller, so.... */
1908         HoldErrs = TRUE;
1909         CurEnv->e_errormode = EM_MAIL;
1910         Verbose = 0;
1911         DisConnected = TRUE;
1912
1913         /* all input from /dev/null */
1914         if (InChannel != stdin)
1915         {
1916                 (void) fclose(InChannel);
1917                 InChannel = stdin;
1918         }
1919         if (freopen("/dev/null", "r", stdin) == NULL)
1920                 sm_syslog(LOG_ERR, e->e_id,
1921                           "disconnect: freopen(\"/dev/null\") failed: %s",
1922                           errstring(errno));
1923
1924         /* output to the transcript */
1925         if (OutChannel != stdout)
1926         {
1927                 (void) fclose(OutChannel);
1928                 OutChannel = stdout;
1929         }
1930         if (droplev > 0)
1931         {
1932                 if (e->e_xfp == NULL)
1933                 {
1934                         fd = open("/dev/null", O_WRONLY, 0666);
1935                         if (fd == -1)
1936                                 sm_syslog(LOG_ERR, e->e_id,
1937                                           "disconnect: open(\"/dev/null\") failed: %s",
1938                                           errstring(errno));
1939                 }
1940                 else
1941                 {
1942                         fd = fileno(e->e_xfp);
1943                         if (fd == -1)
1944                                 sm_syslog(LOG_ERR, e->e_id,
1945                                           "disconnect: fileno(e->e_xfp) failed: %s",
1946                                           errstring(errno));
1947                 }
1948                 (void) fflush(stdout);
1949                 dup2(fd, STDOUT_FILENO);
1950                 dup2(fd, STDERR_FILENO);
1951                 if (e->e_xfp == NULL)
1952                         close(fd);
1953         }
1954
1955         /* drop our controlling TTY completely if possible */
1956         if (droplev > 1)
1957         {
1958                 (void) setsid();
1959                 errno = 0;
1960         }
1961
1962 #if XDEBUG
1963         checkfd012("disconnect");
1964 #endif
1965
1966         if (LogLevel > 71)
1967                 sm_syslog(LOG_DEBUG, e->e_id,
1968                         "in background, pid=%d",
1969                         getpid());
1970
1971         errno = 0;
1972 }
1973
1974 static void
1975 obsolete(argv)
1976         char *argv[];
1977 {
1978         register char *ap;
1979         register char *op;
1980
1981         while ((ap = *++argv) != NULL)
1982         {
1983                 /* Return if "--" or not an option of any form. */
1984                 if (ap[0] != '-' || ap[1] == '-')
1985                         return;
1986
1987                 /* skip over options that do have a value */
1988                 op = strchr(OPTIONS, ap[1]);
1989                 if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1990                     ap[1] != 'd' &&
1991 #if defined(sony_news)
1992                     ap[1] != 'E' && ap[1] != 'J' &&
1993 #endif
1994                     argv[1] != NULL && argv[1][0] != '-')
1995                 {
1996                         argv++;
1997                         continue;
1998                 }
1999
2000                 /* If -C doesn't have an argument, use sendmail.cf. */
2001 #define __DEFPATH       "sendmail.cf"
2002                 if (ap[1] == 'C' && ap[2] == '\0')
2003                 {
2004                         *argv = xalloc(sizeof(__DEFPATH) + 2);
2005                         argv[0][0] = '-';
2006                         argv[0][1] = 'C';
2007                         (void)strcpy(&argv[0][2], __DEFPATH);
2008                 }
2009
2010                 /* If -q doesn't have an argument, run it once. */
2011                 if (ap[1] == 'q' && ap[2] == '\0')
2012                         *argv = "-q0";
2013
2014                 /* if -d doesn't have an argument, use 0-99.1 */
2015                 if (ap[1] == 'd' && ap[2] == '\0')
2016                         *argv = "-d0-99.1";
2017
2018 # if defined(sony_news)
2019                 /* if -E doesn't have an argument, use -EC */
2020                 if (ap[1] == 'E' && ap[2] == '\0')
2021                         *argv = "-EC";
2022
2023                 /* if -J doesn't have an argument, use -JJ */
2024                 if (ap[1] == 'J' && ap[2] == '\0')
2025                         *argv = "-JJ";
2026 # endif
2027         }
2028 }
2029 \f/*
2030 **  AUTH_WARNING -- specify authorization warning
2031 **
2032 **      Parameters:
2033 **              e -- the current envelope.
2034 **              msg -- the text of the message.
2035 **              args -- arguments to the message.
2036 **
2037 **      Returns:
2038 **              none.
2039 */
2040
2041 void
2042 #ifdef __STDC__
2043 auth_warning(register ENVELOPE *e, const char *msg, ...)
2044 #else
2045 auth_warning(e, msg, va_alist)
2046         register ENVELOPE *e;
2047         const char *msg;
2048         va_dcl
2049 #endif
2050 {
2051         char buf[MAXLINE];
2052         VA_LOCAL_DECL
2053
2054         if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2055         {
2056                 register char *p;
2057                 static char hostbuf[48];
2058                 extern struct hostent *myhostname __P((char *, int));
2059
2060                 if (hostbuf[0] == '\0')
2061                         (void) myhostname(hostbuf, sizeof hostbuf);
2062
2063                 (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2064                 p = &buf[strlen(buf)];
2065                 VA_START(msg);
2066                 vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2067                 VA_END;
2068                 addheader("X-Authentication-Warning", buf, &e->e_header);
2069                 if (LogLevel > 3)
2070                         sm_syslog(LOG_INFO, e->e_id,
2071                                 "Authentication-Warning: %.400s",
2072                                 buf);
2073         }
2074 }
2075 \f/*
2076 **  GETEXTENV -- get from external environment
2077 **
2078 **      Parameters:
2079 **              envar -- the name of the variable to retrieve
2080 **
2081 **      Returns:
2082 **              The value, if any.
2083 */
2084
2085 char *
2086 getextenv(envar)
2087         const char *envar;
2088 {
2089         char **envp;
2090         int l;
2091
2092         l = strlen(envar);
2093         for (envp = ExternalEnviron; *envp != NULL; envp++)
2094         {
2095                 if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2096                         return &(*envp)[l + 1];
2097         }
2098         return NULL;
2099 }
2100 \f/*
2101 **  SETUSERENV -- set an environment in the propogated environment
2102 **
2103 **      Parameters:
2104 **              envar -- the name of the environment variable.
2105 **              value -- the value to which it should be set.  If
2106 **                      null, this is extracted from the incoming
2107 **                      environment.  If that is not set, the call
2108 **                      to setuserenv is ignored.
2109 **
2110 **      Returns:
2111 **              none.
2112 */
2113
2114 void
2115 setuserenv(envar, value)
2116         const char *envar;
2117         const char *value;
2118 {
2119         int i;
2120         char **evp = UserEnviron;
2121         char *p;
2122
2123         if (value == NULL)
2124         {
2125                 value = getextenv(envar);
2126                 if (value == NULL)
2127                         return;
2128         }
2129
2130         i = strlen(envar);
2131         p = (char *) xalloc(strlen(value) + i + 2);
2132         strcpy(p, envar);
2133         p[i++] = '=';
2134         strcpy(&p[i], value);
2135
2136         while (*evp != NULL && strncmp(*evp, p, i) != 0)
2137                 evp++;
2138         if (*evp != NULL)
2139         {
2140                 *evp++ = p;
2141         }
2142         else if (evp < &UserEnviron[MAXUSERENVIRON])
2143         {
2144                 *evp++ = p;
2145                 *evp = NULL;
2146         }
2147
2148         /* make sure it is in our environment as well */
2149         if (putenv(p) < 0)
2150                 syserr("setuserenv: putenv(%s) failed", p);
2151 }
2152 \f/*
2153 **  DUMPSTATE -- dump state
2154 **
2155 **      For debugging.
2156 */
2157
2158 void
2159 dumpstate(when)
2160         char *when;
2161 {
2162         register char *j = macvalue('j', CurEnv);
2163         int rs;
2164
2165         sm_syslog(LOG_DEBUG, CurEnv->e_id,
2166                 "--- dumping state on %s: $j = %s ---",
2167                 when,
2168                 j == NULL ? "<NULL>" : j);
2169         if (j != NULL)
2170         {
2171                 if (!wordinclass(j, 'w'))
2172                         sm_syslog(LOG_DEBUG, CurEnv->e_id,
2173                                 "*** $j not in $=w ***");
2174         }
2175         sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2176         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2177         printopenfds(TRUE);
2178         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2179         mci_dump_all(TRUE);
2180         rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2181         if (rs > 0)
2182         {
2183                 int stat;
2184                 register char **pvp;
2185                 char *pv[MAXATOM + 1];
2186
2187                 pv[0] = NULL;
2188                 stat = rewrite(pv, rs, 0, CurEnv);
2189                 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2190                        "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2191                        stat);
2192                 for (pvp = pv; *pvp != NULL; pvp++)
2193                         sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2194         }
2195         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2196 }
2197
2198
2199 /* ARGSUSED */
2200 SIGFUNC_DECL
2201 sigusr1(sig)
2202         int sig;
2203 {
2204         dumpstate("user signal");
2205         return SIGFUNC_RETURN;
2206 }
2207
2208
2209 /* ARGSUSED */
2210 SIGFUNC_DECL
2211 sighup(sig)
2212         int sig;
2213 {
2214         if (SaveArgv[0][0] != '/')
2215         {
2216                 if (LogLevel > 3)
2217                         sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
2218                 finis(FALSE, EX_OSFILE);
2219         }
2220         if (LogLevel > 3)
2221                 sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
2222         alarm(0);
2223         releasesignal(SIGHUP);
2224         closecontrolsocket(TRUE);
2225         if (drop_privileges(TRUE) != EX_OK)
2226         {
2227                 if (LogLevel > 0)
2228                         sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
2229                                 RunAsUid, RunAsGid);
2230                 finis(FALSE, EX_OSERR);
2231         }
2232         execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
2233         if (LogLevel > 0)
2234                 sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
2235         finis(FALSE, EX_OSFILE);
2236 }
2237 \f/*
2238 **  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2239 **
2240 **      Parameters:
2241 **              to_real_uid -- if set, drop to the real uid instead
2242 **                      of the RunAsUser.
2243 **
2244 **      Returns:
2245 **              EX_OSERR if the setuid failed.
2246 **              EX_OK otherwise.
2247 */
2248
2249 int
2250 drop_privileges(to_real_uid)
2251         bool to_real_uid;
2252 {
2253         int rval = EX_OK;
2254         GIDSET_T emptygidset[1];
2255
2256         if (tTd(47, 1))
2257                 printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2258                         (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
2259
2260         if (to_real_uid)
2261         {
2262                 RunAsUserName = RealUserName;
2263                 RunAsUid = RealUid;
2264                 RunAsGid = RealGid;
2265         }
2266
2267         /* make sure no one can grab open descriptors for secret files */
2268         endpwent();
2269
2270         /* reset group permissions; these can be set later */
2271         emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2272         if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2273                 rval = EX_OSERR;
2274
2275         /* reset primary group and user id */
2276         if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2277                 rval = EX_OSERR;
2278         if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
2279                 rval = EX_OSERR;
2280         if (tTd(47, 5))
2281         {
2282                 printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2283                         (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
2284                 printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2285         }
2286         return rval;
2287 }
2288 \f/*
2289 **  FILL_FD -- make sure a file descriptor has been properly allocated
2290 **
2291 **      Used to make sure that stdin/out/err are allocated on startup
2292 **
2293 **      Parameters:
2294 **              fd -- the file descriptor to be filled.
2295 **              where -- a string used for logging.  If NULL, this is
2296 **                      being called on startup, and logging should
2297 **                      not be done.
2298 **
2299 **      Returns:
2300 **              none
2301 */
2302
2303 void
2304 fill_fd(fd, where)
2305         int fd;
2306         char *where;
2307 {
2308         int i;
2309         struct stat stbuf;
2310
2311         if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2312                 return;
2313
2314         if (where != NULL)
2315                 syserr("fill_fd: %s: fd %d not open", where, fd);
2316         else
2317                 MissingFds |= 1 << fd;
2318         i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2319         if (i < 0)
2320         {
2321                 syserr("!fill_fd: %s: cannot open /dev/null",
2322                         where == NULL ? "startup" : where);
2323         }
2324         if (fd != i)
2325         {
2326                 (void) dup2(i, fd);
2327                 (void) close(i);
2328         }
2329 }
2330 \f/*
2331 **  TESTMODELINE -- process a test mode input line
2332 **
2333 **      Parameters:
2334 **              line -- the input line.
2335 **              e -- the current environment.
2336 **      Syntax:
2337 **              #  a comment
2338 **              .X process X as a configuration line
2339 **              =X dump a configuration item (such as mailers)
2340 **              $X dump a macro or class
2341 **              /X try an activity
2342 **              X  normal process through rule set X
2343 */
2344
2345 void
2346 testmodeline(line, e)
2347         char *line;
2348         ENVELOPE *e;
2349 {
2350         register char *p;
2351         char *q;
2352         auto char *delimptr;
2353         int mid;
2354         int i, rs;
2355         STAB *map;
2356         char **s;
2357         struct rewrite *rw;
2358         ADDRESS a;
2359         static int tryflags = RF_COPYNONE;
2360         char exbuf[MAXLINE];
2361         extern bool invalidaddr __P((char *, char *));
2362         extern char *crackaddr __P((char *));
2363         extern void dump_class __P((STAB *, int));
2364         extern void translate_dollars __P((char *));
2365         extern void help __P((char *));
2366
2367         switch (line[0])
2368         {
2369           case '#':
2370           case 0:
2371                 return;
2372
2373           case '?':
2374                 help("-bt");
2375                 return;
2376
2377           case '.':             /* config-style settings */
2378                 switch (line[1])
2379                 {
2380                   case 'D':
2381                         mid = macid(&line[2], &delimptr);
2382                         if (mid == '\0')
2383                                 return;
2384                         translate_dollars(delimptr);
2385                         define(mid, newstr(delimptr), e);
2386                         break;
2387
2388                   case 'C':
2389                         if (line[2] == '\0')    /* not to call syserr() */
2390                                 return;
2391
2392                         mid = macid(&line[2], &delimptr);
2393                         if (mid == '\0')
2394                                 return;
2395                         translate_dollars(delimptr);
2396                         expand(delimptr, exbuf, sizeof exbuf, e);
2397                         p = exbuf;
2398                         while (*p != '\0')
2399                         {
2400                                 register char *wd;
2401                                 char delim;
2402
2403                                 while (*p != '\0' && isascii(*p) && isspace(*p))
2404                                         p++;
2405                                 wd = p;
2406                                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2407                                         p++;
2408                                 delim = *p;
2409                                 *p = '\0';
2410                                 if (wd[0] != '\0')
2411                                         setclass(mid, wd);
2412                                 *p = delim;
2413                         }
2414                         break;
2415
2416                   case '\0':
2417                         printf("Usage: .[DC]macro value(s)\n");
2418                         break;
2419
2420                   default:
2421                         printf("Unknown \".\" command %s\n", line);
2422                         break;
2423                 }
2424                 return;
2425
2426           case '=':             /* config-style settings */
2427                 switch (line[1])
2428                 {
2429                   case 'S':             /* dump rule set */
2430                         rs = strtorwset(&line[2], NULL, ST_FIND);
2431                         if (rs < 0)
2432                         {
2433                                 printf("Undefined ruleset %s\n", &line[2]);
2434                                 return;
2435                         }
2436                         rw = RewriteRules[rs];
2437                         if (rw == NULL)
2438                                 return;
2439                         do
2440                         {
2441                                 putchar('R');
2442                                 s = rw->r_lhs;
2443                                 while (*s != NULL)
2444                                 {
2445                                         xputs(*s++);
2446                                         putchar(' ');
2447                                 }
2448                                 putchar('\t');
2449                                 putchar('\t');
2450                                 s = rw->r_rhs;
2451                                 while (*s != NULL)
2452                                 {
2453                                         xputs(*s++);
2454                                         putchar(' ');
2455                                 }
2456                                 putchar('\n');
2457                         } while ((rw = rw->r_next) != NULL);
2458                         break;
2459
2460                   case 'M':
2461                         for (i = 0; i < MAXMAILERS; i++)
2462                         {
2463                                 if (Mailer[i] != NULL)
2464                                         printmailer(Mailer[i]);
2465                         }
2466                         break;
2467
2468                   case '\0':
2469                         printf("Usage: =Sruleset or =M\n");
2470                         break;
2471
2472                   default:
2473                         printf("Unknown \"=\" command %s\n", line);
2474                         break;
2475                 }
2476                 return;
2477
2478           case '-':             /* set command-line-like opts */
2479                 switch (line[1])
2480                 {
2481                   case 'd':
2482                         tTflag(&line[2]);
2483                         break;
2484
2485                   case '\0':
2486                         printf("Usage: -d{debug arguments}\n");
2487                         break;
2488
2489                   default:
2490                         printf("Unknown \"-\" command %s\n", line);
2491                         break;
2492                 }
2493                 return;
2494
2495           case '$':
2496                 if (line[1] == '=')
2497                 {
2498                         mid = macid(&line[2], NULL);
2499                         if (mid != '\0')
2500                                 stabapply(dump_class, mid);
2501                         return;
2502                 }
2503                 mid = macid(&line[1], NULL);
2504                 if (mid == '\0')
2505                         return;
2506                 p = macvalue(mid, e);
2507                 if (p == NULL)
2508                         printf("Undefined\n");
2509                 else
2510                 {
2511                         xputs(p);
2512                         printf("\n");
2513                 }
2514                 return;
2515
2516           case '/':             /* miscellaneous commands */
2517                 p = &line[strlen(line)];
2518                 while (--p >= line && isascii(*p) && isspace(*p))
2519                         *p = '\0';
2520                 p = strpbrk(line, " \t");
2521                 if (p != NULL)
2522                 {
2523                         while (isascii(*p) && isspace(*p))
2524                                 *p++ = '\0';
2525                 }
2526                 else
2527                         p = "";
2528                 if (line[1] == '\0')
2529                 {
2530                         printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
2531                         return;
2532                 }
2533                 if (strcasecmp(&line[1], "mx") == 0)
2534                 {
2535 #if NAMED_BIND
2536                         /* look up MX records */
2537                         int nmx;
2538                         auto int rcode;
2539                         char *mxhosts[MAXMXHOSTS + 1];
2540
2541                         if (*p == '\0')
2542                         {
2543                                 printf("Usage: /mx address\n");
2544                                 return;
2545                         }
2546                         nmx = getmxrr(p, mxhosts, FALSE, &rcode);
2547                         printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
2548                         for (i = 0; i < nmx; i++)
2549                                 printf("\t%s\n", mxhosts[i]);
2550 #else
2551                         printf("No MX code compiled in\n");
2552 #endif
2553                 }
2554                 else if (strcasecmp(&line[1], "canon") == 0)
2555                 {
2556                         char host[MAXHOSTNAMELEN];
2557
2558                         if (*p == '\0')
2559                         {
2560                                 printf("Usage: /canon address\n");
2561                                 return;
2562                         }
2563                         else if (strlen(p) >= sizeof host)
2564                         {
2565                                 printf("Name too long\n");
2566                                 return;
2567                         }
2568                         strcpy(host, p);
2569                         (void) getcanonname(host, sizeof(host), HasWildcardMX);
2570                         printf("getcanonname(%s) returns %s\n", p, host);
2571                 }
2572                 else if (strcasecmp(&line[1], "map") == 0)
2573                 {
2574                         auto int rcode = EX_OK;
2575                         char *av[2];
2576
2577                         if (*p == '\0')
2578                         {
2579                                 printf("Usage: /map mapname key\n");
2580                                 return;
2581                         }
2582                         for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
2583                                 continue;
2584                         if (*q == '\0')
2585                         {
2586                                 printf("No key specified\n");
2587                                 return;
2588                         }
2589                         *q++ = '\0';
2590                         map = stab(p, ST_MAP, ST_FIND);
2591                         if (map == NULL)
2592                         {
2593                                 printf("Map named \"%s\" not found\n", p);
2594                                 return;
2595                         }
2596                         if (!bitset(MF_OPEN, map->s_map.map_mflags))
2597                         {
2598                                 printf("Map named \"%s\" not open\n", p);
2599                                 return;
2600                         }
2601                         printf("map_lookup: %s (%s) ", p, q);
2602                         av[0] = q;
2603                         av[1] = NULL;
2604                         p = (*map->s_map.map_class->map_lookup)
2605                                         (&map->s_map, q, av, &rcode);
2606                         if (p == NULL)
2607                                 printf("no match (%d)\n", rcode);
2608                         else
2609                                 printf("returns %s (%d)\n", p, rcode);
2610                 }
2611                 else if (strcasecmp(&line[1], "try") == 0)
2612                 {
2613                         MAILER *m;
2614                         STAB *s;
2615                         auto int rcode = EX_OK;
2616
2617                         q = strpbrk(p, " \t");
2618                         if (q != NULL)
2619                         {
2620                                 while (isascii(*q) && isspace(*q))
2621                                         *q++ = '\0';
2622                         }
2623                         if (q == NULL || *q == '\0')
2624                         {
2625                                 printf("Usage: /try mailer address\n");
2626                                 return;
2627                         }
2628                         s = stab(p, ST_MAILER, ST_FIND);
2629                         if (s == NULL)
2630                         {
2631                                 printf("Unknown mailer %s\n", p);
2632                                 return;
2633                         }
2634                         m = s->s_mailer;
2635                         printf("Trying %s %s address %s for mailer %s\n",
2636                                 bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2637                                 bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
2638                                 q, p);
2639                         p = remotename(q, m, tryflags, &rcode, CurEnv);
2640                         printf("Rcode = %d, addr = %s\n",
2641                                 rcode, p == NULL ? "<NULL>" : p);
2642                         e->e_to = NULL;
2643                 }
2644                 else if (strcasecmp(&line[1], "tryflags") == 0)
2645                 {
2646                         if (*p == '\0')
2647                         {
2648                                 printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
2649                                 return;
2650                         }
2651                         for (; *p != '\0'; p++)
2652                         {
2653                                 switch (*p)
2654                                 {
2655                                   case 'H':
2656                                   case 'h':
2657                                         tryflags |= RF_HEADERADDR;
2658                                         break;
2659
2660                                   case 'E':
2661                                   case 'e':
2662                                         tryflags &= ~RF_HEADERADDR;
2663                                         break;
2664
2665                                   case 'S':
2666                                   case 's':
2667                                         tryflags |= RF_SENDERADDR;
2668                                         break;
2669
2670                                   case 'R':
2671                                   case 'r':
2672                                         tryflags &= ~RF_SENDERADDR;
2673                                         break;
2674                                 }
2675                         }
2676                 }
2677                 else if (strcasecmp(&line[1], "parse") == 0)
2678                 {
2679                         if (*p == '\0')
2680                         {
2681                                 printf("Usage: /parse address\n");
2682                                 return;
2683                         }
2684                         q = crackaddr(p);
2685                         printf("Cracked address = ");
2686                         xputs(q);
2687                         printf("\nParsing %s %s address\n",
2688                                 bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2689                                 bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
2690                         if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
2691                                 printf("Cannot parse\n");
2692                         else if (a.q_host != NULL && a.q_host[0] != '\0')
2693                                 printf("mailer %s, host %s, user %s\n",
2694                                         a.q_mailer->m_name, a.q_host, a.q_user);
2695                         else
2696                                 printf("mailer %s, user %s\n",
2697                                         a.q_mailer->m_name, a.q_user);
2698                         e->e_to = NULL;
2699                 }
2700                 else
2701                 {
2702                         printf("Unknown \"/\" command %s\n", line);
2703                 }
2704                 return;
2705         }
2706
2707         for (p = line; isascii(*p) && isspace(*p); p++)
2708                 continue;
2709         q = p;
2710         while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2711                 p++;
2712         if (*p == '\0')
2713         {
2714                 printf("No address!\n");
2715                 return;
2716         }
2717         *p = '\0';
2718         if (invalidaddr(p + 1, NULL))
2719                 return;
2720         do
2721         {
2722                 register char **pvp;
2723                 char pvpbuf[PSBUFSIZE];
2724
2725                 pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
2726                               &delimptr, NULL);
2727                 if (pvp == NULL)
2728                         continue;
2729                 p = q;
2730                 while (*p != '\0')
2731                 {
2732                         int stat;
2733
2734                         rs = strtorwset(p, NULL, ST_FIND);
2735                         if (rs < 0)
2736                         {
2737                                 printf("Undefined ruleset %s\n", p);
2738                                 break;
2739                         }
2740                         stat = rewrite(pvp, rs, 0, e);
2741                         if (stat != EX_OK)
2742                                 printf("== Ruleset %s (%d) status %d\n",
2743                                         p, rs, stat);
2744                         while (*p != '\0' && *p++ != ',')
2745                                 continue;
2746                 }
2747         } while (*(p = delimptr) != '\0');
2748 }
2749
2750
2751 void
2752 dump_class(s, id)
2753         register STAB *s;
2754         int id;
2755 {
2756         if (s->s_type != ST_CLASS)
2757                 return;
2758         if (bitnset(id & 0xff, s->s_class))
2759                 printf("%s\n", s->s_name);
2760 }