main.c revision 38032
138032Speter/*
238032Speter * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
438032Speter * Copyright (c) 1988, 1993
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1338032Speter#ifndef lint
1438032Speterstatic char copyright[] =
1538032Speter"@(#) Copyright (c) 1998 Sendmail, Inc.  All rights reserved.\n\
1638032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
1738032Speter     Copyright (c) 1988, 1993\n\
1838032Speter	The Regents of the University of California.  All rights reserved.\n";
1938032Speter#endif /* not lint */
2038032Speter
2138032Speter#ifndef lint
2238032Speterstatic char sccsid[] = "@(#)main.c	8.302 (Berkeley) 6/4/98";
2338032Speter#endif /* not lint */
2438032Speter
2538032Speter#define	_DEFINE
2638032Speter
2738032Speter#include "sendmail.h"
2838032Speter#include <arpa/inet.h>
2938032Speter#include <grp.h>
3038032Speter#if NAMED_BIND
3138032Speter#include <resolv.h>
3238032Speter#endif
3338032Speter
3438032Speter/*
3538032Speter**  SENDMAIL -- Post mail to a set of destinations.
3638032Speter**
3738032Speter**	This is the basic mail router.  All user mail programs should
3838032Speter**	call this routine to actually deliver mail.  Sendmail in
3938032Speter**	turn calls a bunch of mail servers that do the real work of
4038032Speter**	delivering the mail.
4138032Speter**
4238032Speter**	Sendmail is driven by settings read in from /etc/sendmail.cf
4338032Speter**	(read by readcf.c).
4438032Speter**
4538032Speter**	Usage:
4638032Speter**		/usr/lib/sendmail [flags] addr ...
4738032Speter**
4838032Speter**		See the associated documentation for details.
4938032Speter**
5038032Speter**	Author:
5138032Speter**		Eric Allman, UCB/INGRES (until 10/81).
5238032Speter**			     Britton-Lee, Inc., purveyors of fine
5338032Speter**				database computers (11/81 - 10/88).
5438032Speter**			     International Computer Science Institute
5538032Speter**				(11/88 - 9/89).
5638032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
5738032Speter**			     InReference, Inc. (8/95 - 1/97).
5838032Speter**			     Sendmail, Inc. (1/98 - present).
5938032Speter**		The support of the my employers is gratefully acknowledged.
6038032Speter**			Few of them (Britton-Lee in particular) have had
6138032Speter**			anything to gain from my involvement in this project.
6238032Speter*/
6338032Speter
6438032Speter
6538032Speterint		NextMailer;	/* "free" index into Mailer struct */
6638032Speterchar		*FullName;	/* sender's full name */
6738032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
6838032SpeterENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
6938032SpeterADDRESS		NullAddress =	/* a null address */
7038032Speter		{ "", "", NULL, "" };
7138032Speterchar		*CommandLineArgs;	/* command line args for pid file */
7238032Speterbool		Warn_Q_option = FALSE;	/* warn about Q option use */
7338032Speterchar		**SaveArgv;	/* argument vector for re-execing */
7438032Speterint		MissingFds = 0;	/* bit map of fds missing on startup */
7538032Speter
7638032Speter#ifdef NGROUPS_MAX
7738032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
7838032Speter#endif
7938032Speter
8038032Speterstatic void	obsolete __P((char **));
8138032Speterextern void	printmailer __P((MAILER *));
8238032Speterextern void	tTflag __P((char *));
8338032Speter
8438032Speter#if DAEMON && !SMTP
8538032SpeterERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
8638032Speter#endif /* DAEMON && !SMTP */
8738032Speter#if SMTP && !QUEUE
8838032SpeterERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
8938032Speter#endif /* DAEMON && !SMTP */
9038032Speter
9138032Speter#define MAXCONFIGLEVEL	8	/* highest config version level known */
9238032Speter
9338032Speterint
9438032Spetermain(argc, argv, envp)
9538032Speter	int argc;
9638032Speter	char **argv;
9738032Speter	char **envp;
9838032Speter{
9938032Speter	register char *p;
10038032Speter	char **av;
10138032Speter	extern char Version[];
10238032Speter	char *ep, *from;
10338032Speter	STAB *st;
10438032Speter	register int i;
10538032Speter	int j;
10638032Speter	bool queuemode = FALSE;		/* process queue requests */
10738032Speter	bool safecf = TRUE;
10838032Speter	bool warn_C_flag = FALSE;
10938032Speter	char warn_f_flag = '\0';
11038032Speter	bool run_in_foreground = FALSE;	/* -bD mode */
11138032Speter	static bool reenter = FALSE;
11238032Speter	struct passwd *pw;
11338032Speter	struct hostent *hp;
11438032Speter	char *nullserver = NULL;
11538032Speter	bool forged;
11638032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
11738032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
11838032Speter	char *emptyenviron[1];
11938032Speter	QUEUE_CHAR *new;
12038032Speter	extern int DtableSize;
12138032Speter	extern int optind;
12238032Speter	extern int opterr;
12338032Speter	extern char *optarg;
12438032Speter	extern char **environ;
12538032Speter	extern time_t convtime __P((char *, char));
12638032Speter	extern SIGFUNC_DECL intsig __P((int));
12738032Speter	extern struct hostent *myhostname __P((char *, int));
12838032Speter	extern char *getauthinfo __P((int, bool *));
12938032Speter	extern char *getcfname __P((void));
13038032Speter	extern SIGFUNC_DECL sigusr1 __P((int));
13138032Speter	extern SIGFUNC_DECL sighup __P((int));
13238032Speter	extern void initmacros __P((ENVELOPE *));
13338032Speter	extern void init_md __P((int, char **));
13438032Speter	extern int getdtsize __P((void));
13538032Speter	extern void tTsetup __P((u_char *, int, char *));
13638032Speter	extern void setdefaults __P((ENVELOPE *));
13738032Speter	extern void initsetproctitle __P((int, char **, char **));
13838032Speter	extern void init_vendor_macros __P((ENVELOPE *));
13938032Speter	extern void load_if_names __P((void));
14038032Speter	extern void vendor_pre_defaults __P((ENVELOPE *));
14138032Speter	extern void vendor_post_defaults __P((ENVELOPE *));
14238032Speter	extern void readcf __P((char *, bool, ENVELOPE *));
14338032Speter	extern void printqueue __P((void));
14438032Speter	extern void sendtoargv __P((char **, ENVELOPE *));
14538032Speter	extern void resetlimits __P((void));
14638032Speter#ifndef HASUNSETENV
14738032Speter	extern void unsetenv __P((char *));
14838032Speter#endif
14938032Speter
15038032Speter	/*
15138032Speter	**  Check to see if we reentered.
15238032Speter	**	This would normally happen if e_putheader or e_putbody
15338032Speter	**	were NULL when invoked.
15438032Speter	*/
15538032Speter
15638032Speter	if (reenter)
15738032Speter	{
15838032Speter		syserr("main: reentered!");
15938032Speter		abort();
16038032Speter	}
16138032Speter	reenter = TRUE;
16238032Speter
16338032Speter	/* avoid null pointer dereferences */
16438032Speter	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
16538032Speter
16638032Speter	/* do machine-dependent initializations */
16738032Speter	init_md(argc, argv);
16838032Speter
16938032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
17038032Speter	DtableSize = getdtsize();
17138032Speter	if (DtableSize > 256)
17238032Speter		DtableSize = 256;
17338032Speter
17438032Speter	/*
17538032Speter	**  Be sure we have enough file descriptors.
17638032Speter	**	But also be sure that 0, 1, & 2 are open.
17738032Speter	*/
17838032Speter
17938032Speter	fill_fd(STDIN_FILENO, NULL);
18038032Speter	fill_fd(STDOUT_FILENO, NULL);
18138032Speter	fill_fd(STDERR_FILENO, NULL);
18238032Speter
18338032Speter	i = DtableSize;
18438032Speter	while (--i > 0)
18538032Speter	{
18638032Speter		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
18738032Speter			(void) close(i);
18838032Speter	}
18938032Speter	errno = 0;
19038032Speter
19138032Speter#if LOG
19238032Speter# ifdef LOG_MAIL
19338032Speter	openlog("sendmail", LOG_PID, LOG_MAIL);
19438032Speter# else
19538032Speter	openlog("sendmail", LOG_PID);
19638032Speter# endif
19738032Speter#endif
19838032Speter
19938032Speter	if (MissingFds != 0)
20038032Speter	{
20138032Speter		char mbuf[MAXLINE];
20238032Speter
20338032Speter		mbuf[0] = '\0';
20438032Speter		if (bitset(1 << STDIN_FILENO, MissingFds))
20538032Speter			strcat(mbuf, ", stdin");
20638032Speter		if (bitset(1 << STDOUT_FILENO, MissingFds))
20738032Speter			strcat(mbuf, ", stdout");
20838032Speter		if (bitset(1 << STDERR_FILENO, MissingFds))
20938032Speter			strcat(mbuf, ", stderr");
21038032Speter		syserr("File descriptors missing on startup: %s", &mbuf[2]);
21138032Speter	}
21238032Speter
21338032Speter	/* reset status from syserr() calls for missing file descriptors */
21438032Speter	Errors = 0;
21538032Speter	ExitStat = EX_OK;
21638032Speter
21738032Speter#if XDEBUG
21838032Speter	checkfd012("after openlog");
21938032Speter#endif
22038032Speter
22138032Speter	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
22238032Speter
22338032Speter#ifdef NGROUPS_MAX
22438032Speter	/* save initial group set for future checks */
22538032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
22638032Speter	if (i == 0)
22738032Speter		InitialGidSet[0] = (GID_T) -1;
22838032Speter	while (i < NGROUPS_MAX)
22938032Speter		InitialGidSet[i++] = InitialGidSet[0];
23038032Speter#endif
23138032Speter
23238032Speter	/* drop group id privileges (RunAsUser not yet set) */
23338032Speter	(void) drop_privileges(FALSE);
23438032Speter
23538032Speter#ifdef SIGUSR1
23638032Speter	/* arrange to dump state on user-1 signal */
23738032Speter	setsignal(SIGUSR1, sigusr1);
23838032Speter#endif
23938032Speter
24038032Speter	/* initialize for setproctitle */
24138032Speter	initsetproctitle(argc, argv, envp);
24238032Speter
24338032Speter	/* Handle any non-getoptable constructions. */
24438032Speter	obsolete(argv);
24538032Speter
24638032Speter	/*
24738032Speter	**  Do a quick prescan of the argument list.
24838032Speter	*/
24938032Speter
25038032Speter#if defined(__osf__) || defined(_AIX3)
25138032Speter# define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
25238032Speter#endif
25338032Speter#if defined(sony_news)
25438032Speter# define OPTIONS	"B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
25538032Speter#endif
25638032Speter#ifndef OPTIONS
25738032Speter# define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
25838032Speter#endif
25938032Speter	opterr = 0;
26038032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
26138032Speter	{
26238032Speter		switch (j)
26338032Speter		{
26438032Speter		  case 'd':
26538032Speter			/* hack attack -- see if should use ANSI mode */
26638032Speter			if (strcmp(optarg, "ANSI") == 0)
26738032Speter			{
26838032Speter				TermEscape.te_rv_on = "\033[7m";
26938032Speter				TermEscape.te_rv_off = "\033[0m";
27038032Speter				break;
27138032Speter			}
27238032Speter			tTflag(optarg);
27338032Speter			setbuf(stdout, (char *) NULL);
27438032Speter			break;
27538032Speter		}
27638032Speter	}
27738032Speter	opterr = 1;
27838032Speter
27938032Speter	/* set up the blank envelope */
28038032Speter	BlankEnvelope.e_puthdr = putheader;
28138032Speter	BlankEnvelope.e_putbody = putbody;
28238032Speter	BlankEnvelope.e_xfp = NULL;
28338032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
28438032Speter	CurEnv = &BlankEnvelope;
28538032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
28638032Speter
28738032Speter	/*
28838032Speter	**  Set default values for variables.
28938032Speter	**	These cannot be in initialized data space.
29038032Speter	*/
29138032Speter
29238032Speter	setdefaults(&BlankEnvelope);
29338032Speter
29438032Speter	RealUid = getuid();
29538032Speter	RealGid = getgid();
29638032Speter
29738032Speter	pw = sm_getpwuid(RealUid);
29838032Speter	if (pw != NULL)
29938032Speter		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
30038032Speter	else
30138032Speter		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
30238032Speter	RealUserName = rnamebuf;
30338032Speter
30438032Speter	if (tTd(0, 101))
30538032Speter	{
30638032Speter		printf("Version %s\n", Version);
30738032Speter		endpwent();
30838032Speter		setuid(RealUid);
30938032Speter		exit(EX_OK);
31038032Speter	}
31138032Speter
31238032Speter	/*
31338032Speter	**  if running non-setuid binary as non-root, pretend
31438032Speter	**  we are the RunAsUid
31538032Speter	*/
31638032Speter	if (RealUid != 0 && geteuid() == RealUid)
31738032Speter	{
31838032Speter		if (tTd(47, 1))
31938032Speter			printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
32038032Speter				(int)RealUid);
32138032Speter		RunAsUid = RealUid;
32238032Speter	}
32338032Speter	else if (geteuid() != 0)
32438032Speter		RunAsUid = geteuid();
32538032Speter
32638032Speter	if (RealUid != 0 && getegid() == RealGid)
32738032Speter		RunAsGid = RealGid;
32838032Speter
32938032Speter	if (tTd(47, 5))
33038032Speter	{
33138032Speter		printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
33238032Speter			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
33338032Speter		printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
33438032Speter	}
33538032Speter
33638032Speter	/* save command line arguments */
33738032Speter	i = 0;
33838032Speter	for (av = argv; *av != NULL; )
33938032Speter		i += strlen(*av++) + 1;
34038032Speter	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
34138032Speter	CommandLineArgs = xalloc(i);
34238032Speter	p = CommandLineArgs;
34338032Speter	for (av = argv, i = 0; *av != NULL; )
34438032Speter	{
34538032Speter		SaveArgv[i++] = newstr(*av);
34638032Speter		if (av != argv)
34738032Speter			*p++ = ' ';
34838032Speter		strcpy(p, *av++);
34938032Speter		p += strlen(p);
35038032Speter	}
35138032Speter	SaveArgv[i] = NULL;
35238032Speter
35338032Speter	if (tTd(0, 1))
35438032Speter	{
35538032Speter		int ll;
35638032Speter		extern char *CompileOptions[];
35738032Speter
35838032Speter		printf("Version %s\n Compiled with:", Version);
35938032Speter		av = CompileOptions;
36038032Speter		ll = 7;
36138032Speter		while (*av != NULL)
36238032Speter		{
36338032Speter			if (ll + strlen(*av) > 63)
36438032Speter			{
36538032Speter				putchar('\n');
36638032Speter				ll = 0;
36738032Speter			}
36838032Speter			if (ll == 0)
36938032Speter			{
37038032Speter				putchar('\t');
37138032Speter				putchar('\t');
37238032Speter			}
37338032Speter			else
37438032Speter				putchar(' ');
37538032Speter			printf("%s", *av);
37638032Speter			ll += strlen(*av++) + 1;
37738032Speter		}
37838032Speter		putchar('\n');
37938032Speter	}
38038032Speter	if (tTd(0, 10))
38138032Speter	{
38238032Speter		int ll;
38338032Speter		extern char *OsCompileOptions[];
38438032Speter
38538032Speter		printf("    OS Defines:");
38638032Speter		av = OsCompileOptions;
38738032Speter		ll = 7;
38838032Speter		while (*av != NULL)
38938032Speter		{
39038032Speter			if (ll + strlen(*av) > 63)
39138032Speter			{
39238032Speter				putchar('\n');
39338032Speter				ll = 0;
39438032Speter			}
39538032Speter			if (ll == 0)
39638032Speter			{
39738032Speter				putchar('\t');
39838032Speter				putchar('\t');
39938032Speter			}
40038032Speter			else
40138032Speter				putchar(' ');
40238032Speter			printf("%s", *av);
40338032Speter			ll += strlen(*av++) + 1;
40438032Speter		}
40538032Speter		putchar('\n');
40638032Speter#ifdef _PATH_UNIX
40738032Speter		printf("Kernel symbols:\t%s\n", _PATH_UNIX);
40838032Speter#endif
40938032Speter		printf(" Def Conf file:\t%s\n", getcfname());
41038032Speter		printf("      Pid file:\t%s\n", PidFile);
41138032Speter	}
41238032Speter
41338032Speter	InChannel = stdin;
41438032Speter	OutChannel = stdout;
41538032Speter
41638032Speter	/* clear sendmail's environment */
41738032Speter	ExternalEnviron = environ;
41838032Speter	emptyenviron[0] = NULL;
41938032Speter	environ = emptyenviron;
42038032Speter
42138032Speter	/*
42238032Speter	** restore any original TZ setting until TimeZoneSpec has been
42338032Speter	** determined - or early log messages may get bogus time stamps
42438032Speter	*/
42538032Speter	if ((p = getextenv("TZ")) != NULL)
42638032Speter	{
42738032Speter		char *tz;
42838032Speter		int tzlen;
42938032Speter
43038032Speter		tzlen = strlen(p) + 4;
43138032Speter		tz = xalloc(tzlen);
43238032Speter		snprintf(tz, tzlen, "TZ=%s", p);
43338032Speter		putenv(tz);
43438032Speter	}
43538032Speter
43638032Speter	/* prime the child environment */
43738032Speter	setuserenv("AGENT", "sendmail");
43838032Speter
43938032Speter	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
44038032Speter		(void) setsignal(SIGINT, intsig);
44138032Speter	(void) setsignal(SIGTERM, intsig);
44238032Speter	(void) setsignal(SIGPIPE, SIG_IGN);
44338032Speter	OldUmask = umask(022);
44438032Speter	OpMode = MD_DELIVER;
44538032Speter	FullName = getextenv("NAME");
44638032Speter
44738032Speter	/*
44838032Speter	**  Initialize name server if it is going to be used.
44938032Speter	*/
45038032Speter
45138032Speter#if NAMED_BIND
45238032Speter	if (!bitset(RES_INIT, _res.options))
45338032Speter		res_init();
45438032Speter	if (tTd(8, 8))
45538032Speter		_res.options |= RES_DEBUG;
45638032Speter	else
45738032Speter		_res.options &= ~RES_DEBUG;
45838032Speter# ifdef RES_NOALIASES
45938032Speter	_res.options |= RES_NOALIASES;
46038032Speter# endif
46138032Speter#endif
46238032Speter
46338032Speter	errno = 0;
46438032Speter	from = NULL;
46538032Speter
46638032Speter	/* initialize some macros, etc. */
46738032Speter	initmacros(CurEnv);
46838032Speter	init_vendor_macros(CurEnv);
46938032Speter
47038032Speter	/* version */
47138032Speter	define('v', Version, CurEnv);
47238032Speter
47338032Speter	/* hostname */
47438032Speter	hp = myhostname(jbuf, sizeof jbuf);
47538032Speter	if (jbuf[0] != '\0')
47638032Speter	{
47738032Speter		struct	utsname	utsname;
47838032Speter
47938032Speter		if (tTd(0, 4))
48038032Speter			printf("canonical name: %s\n", jbuf);
48138032Speter		define('w', newstr(jbuf), CurEnv);	/* must be new string */
48238032Speter		define('j', newstr(jbuf), CurEnv);
48338032Speter		setclass('w', jbuf);
48438032Speter
48538032Speter		p = strchr(jbuf, '.');
48638032Speter		if (p != NULL)
48738032Speter		{
48838032Speter			if (p[1] != '\0')
48938032Speter			{
49038032Speter				define('m', newstr(&p[1]), CurEnv);
49138032Speter			}
49238032Speter			while (p != NULL && strchr(&p[1], '.') != NULL)
49338032Speter			{
49438032Speter				*p = '\0';
49538032Speter				if (tTd(0, 4))
49638032Speter					printf("\ta.k.a.: %s\n", jbuf);
49738032Speter				setclass('w', jbuf);
49838032Speter				*p++ = '.';
49938032Speter				p = strchr(p, '.');
50038032Speter			}
50138032Speter		}
50238032Speter
50338032Speter		if (uname(&utsname) >= 0)
50438032Speter			p = utsname.nodename;
50538032Speter		else
50638032Speter		{
50738032Speter			if (tTd(0, 22))
50838032Speter				printf("uname failed (%s)\n", errstring(errno));
50938032Speter			makelower(jbuf);
51038032Speter			p = jbuf;
51138032Speter		}
51238032Speter		if (tTd(0, 4))
51338032Speter			printf(" UUCP nodename: %s\n", p);
51438032Speter		p = newstr(p);
51538032Speter		define('k', p, CurEnv);
51638032Speter		setclass('k', p);
51738032Speter		setclass('w', p);
51838032Speter	}
51938032Speter	if (hp != NULL)
52038032Speter	{
52138032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
52238032Speter		{
52338032Speter			if (tTd(0, 4))
52438032Speter				printf("\ta.k.a.: %s\n", *av);
52538032Speter			setclass('w', *av);
52638032Speter		}
52738032Speter#if NETINET
52838032Speter		if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
52938032Speter		{
53038032Speter			for (i = 0; hp->h_addr_list[i] != NULL; i++)
53138032Speter			{
53238032Speter				char ipbuf[103];
53338032Speter
53438032Speter				snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
53538032Speter					inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
53638032Speter				if (tTd(0, 4))
53738032Speter					printf("\ta.k.a.: %s\n", ipbuf);
53838032Speter				setclass('w', ipbuf);
53938032Speter			}
54038032Speter		}
54138032Speter#endif
54238032Speter	}
54338032Speter
54438032Speter	/* current time */
54538032Speter	define('b', arpadate((char *) NULL), CurEnv);
54638032Speter
54738032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
54838032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
54938032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
55038032Speter
55138032Speter	/*
55238032Speter	** Crack argv.
55338032Speter	*/
55438032Speter
55538032Speter	av = argv;
55638032Speter	p = strrchr(*av, '/');
55738032Speter	if (p++ == NULL)
55838032Speter		p = *av;
55938032Speter	if (strcmp(p, "newaliases") == 0)
56038032Speter		OpMode = MD_INITALIAS;
56138032Speter	else if (strcmp(p, "mailq") == 0)
56238032Speter		OpMode = MD_PRINT;
56338032Speter	else if (strcmp(p, "smtpd") == 0)
56438032Speter		OpMode = MD_DAEMON;
56538032Speter	else if (strcmp(p, "hoststat") == 0)
56638032Speter		OpMode = MD_HOSTSTAT;
56738032Speter	else if (strcmp(p, "purgestat") == 0)
56838032Speter		OpMode = MD_PURGESTAT;
56938032Speter
57038032Speter	optind = 1;
57138032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
57238032Speter	{
57338032Speter		switch (j)
57438032Speter		{
57538032Speter		  case 'b':	/* operations mode */
57638032Speter			switch (j = *optarg)
57738032Speter			{
57838032Speter			  case MD_DAEMON:
57938032Speter			  case MD_FGDAEMON:
58038032Speter# if !DAEMON
58138032Speter				usrerr("Daemon mode not implemented");
58238032Speter				ExitStat = EX_USAGE;
58338032Speter				break;
58438032Speter# endif /* DAEMON */
58538032Speter			  case MD_SMTP:
58638032Speter# if !SMTP
58738032Speter				usrerr("I don't speak SMTP");
58838032Speter				ExitStat = EX_USAGE;
58938032Speter				break;
59038032Speter# endif /* SMTP */
59138032Speter
59238032Speter			  case MD_INITALIAS:
59338032Speter			  case MD_DELIVER:
59438032Speter			  case MD_VERIFY:
59538032Speter			  case MD_TEST:
59638032Speter			  case MD_PRINT:
59738032Speter			  case MD_HOSTSTAT:
59838032Speter			  case MD_PURGESTAT:
59938032Speter			  case MD_ARPAFTP:
60038032Speter				OpMode = j;
60138032Speter				break;
60238032Speter
60338032Speter			  case MD_FREEZE:
60438032Speter				usrerr("Frozen configurations unsupported");
60538032Speter				ExitStat = EX_USAGE;
60638032Speter				break;
60738032Speter
60838032Speter			  default:
60938032Speter				usrerr("Invalid operation mode %c", j);
61038032Speter				ExitStat = EX_USAGE;
61138032Speter				break;
61238032Speter			}
61338032Speter			break;
61438032Speter
61538032Speter		  case 'B':	/* body type */
61638032Speter			CurEnv->e_bodytype = optarg;
61738032Speter			break;
61838032Speter
61938032Speter		  case 'C':	/* select configuration file (already done) */
62038032Speter			if (RealUid != 0)
62138032Speter				warn_C_flag = TRUE;
62238032Speter			ConfFile = optarg;
62338032Speter			(void) drop_privileges(TRUE);
62438032Speter			safecf = FALSE;
62538032Speter			break;
62638032Speter
62738032Speter		  case 'd':	/* debugging -- already done */
62838032Speter			break;
62938032Speter
63038032Speter		  case 'f':	/* from address */
63138032Speter		  case 'r':	/* obsolete -f flag */
63238032Speter			if (from != NULL)
63338032Speter			{
63438032Speter				usrerr("More than one \"from\" person");
63538032Speter				ExitStat = EX_USAGE;
63638032Speter				break;
63738032Speter			}
63838032Speter			from = newstr(denlstring(optarg, TRUE, TRUE));
63938032Speter			if (strcmp(RealUserName, from) != 0)
64038032Speter				warn_f_flag = j;
64138032Speter			break;
64238032Speter
64338032Speter		  case 'F':	/* set full name */
64438032Speter			FullName = newstr(optarg);
64538032Speter			break;
64638032Speter
64738032Speter		  case 'h':	/* hop count */
64838032Speter			CurEnv->e_hopcount = strtol(optarg, &ep, 10);
64938032Speter			if (*ep)
65038032Speter			{
65138032Speter				usrerr("Bad hop count (%s)", optarg);
65238032Speter				ExitStat = EX_USAGE;
65338032Speter			}
65438032Speter			break;
65538032Speter
65638032Speter		  case 'n':	/* don't alias */
65738032Speter			NoAlias = TRUE;
65838032Speter			break;
65938032Speter
66038032Speter		  case 'N':	/* delivery status notifications */
66138032Speter			DefaultNotify |= QHASNOTIFY;
66238032Speter			if (strcasecmp(optarg, "never") == 0)
66338032Speter				break;
66438032Speter			for (p = optarg; p != NULL; optarg = p)
66538032Speter			{
66638032Speter				p = strchr(p, ',');
66738032Speter				if (p != NULL)
66838032Speter					*p++ = '\0';
66938032Speter				if (strcasecmp(optarg, "success") == 0)
67038032Speter					DefaultNotify |= QPINGONSUCCESS;
67138032Speter				else if (strcasecmp(optarg, "failure") == 0)
67238032Speter					DefaultNotify |= QPINGONFAILURE;
67338032Speter				else if (strcasecmp(optarg, "delay") == 0)
67438032Speter					DefaultNotify |= QPINGONDELAY;
67538032Speter				else
67638032Speter				{
67738032Speter					usrerr("Invalid -N argument");
67838032Speter					ExitStat = EX_USAGE;
67938032Speter				}
68038032Speter			}
68138032Speter			break;
68238032Speter
68338032Speter		  case 'o':	/* set option */
68438032Speter			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
68538032Speter			break;
68638032Speter
68738032Speter		  case 'O':	/* set option (long form) */
68838032Speter			setoption(' ', optarg, FALSE, TRUE, CurEnv);
68938032Speter			break;
69038032Speter
69138032Speter		  case 'p':	/* set protocol */
69238032Speter			p = strchr(optarg, ':');
69338032Speter			if (p != NULL)
69438032Speter			{
69538032Speter				*p++ = '\0';
69638032Speter				if (*p != '\0')
69738032Speter				{
69838032Speter					ep = xalloc(strlen(p) + 1);
69938032Speter					cleanstrcpy(ep, p, MAXNAME);
70038032Speter					define('s', ep, CurEnv);
70138032Speter				}
70238032Speter			}
70338032Speter			if (*optarg != '\0')
70438032Speter			{
70538032Speter				ep = xalloc(strlen(optarg) + 1);
70638032Speter				cleanstrcpy(ep, optarg, MAXNAME);
70738032Speter				define('r', ep, CurEnv);
70838032Speter			}
70938032Speter			break;
71038032Speter
71138032Speter		  case 'q':	/* run queue files at intervals */
71238032Speter# if QUEUE
71338032Speter			FullName = NULL;
71438032Speter			queuemode = TRUE;
71538032Speter			switch (optarg[0])
71638032Speter			{
71738032Speter			  case 'I':
71838032Speter				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
71938032Speter					syserr("!Out of memory!!");
72038032Speter				new->queue_match = newstr(&optarg[1]);
72138032Speter				new->queue_next = QueueLimitId;
72238032Speter				QueueLimitId = new;
72338032Speter				break;
72438032Speter
72538032Speter			  case 'R':
72638032Speter				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
72738032Speter					syserr("!Out of memory!!");
72838032Speter				new->queue_match = newstr(&optarg[1]);
72938032Speter				new->queue_next = QueueLimitRecipient;
73038032Speter				QueueLimitRecipient = new;
73138032Speter				break;
73238032Speter
73338032Speter			  case 'S':
73438032Speter				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
73538032Speter					syserr("!Out of memory!!");
73638032Speter				new->queue_match = newstr(&optarg[1]);
73738032Speter				new->queue_next = QueueLimitSender;
73838032Speter				QueueLimitSender = new;
73938032Speter				break;
74038032Speter
74138032Speter			  default:
74238032Speter				QueueIntvl = convtime(optarg, 'm');
74338032Speter				break;
74438032Speter			}
74538032Speter# else /* QUEUE */
74638032Speter			usrerr("I don't know about queues");
74738032Speter			ExitStat = EX_USAGE;
74838032Speter# endif /* QUEUE */
74938032Speter			break;
75038032Speter
75138032Speter		  case 'R':	/* DSN RET: what to return */
75238032Speter			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
75338032Speter			{
75438032Speter				usrerr("Duplicate -R flag");
75538032Speter				ExitStat = EX_USAGE;
75638032Speter				break;
75738032Speter			}
75838032Speter			CurEnv->e_flags |= EF_RET_PARAM;
75938032Speter			if (strcasecmp(optarg, "hdrs") == 0)
76038032Speter				CurEnv->e_flags |= EF_NO_BODY_RETN;
76138032Speter			else if (strcasecmp(optarg, "full") != 0)
76238032Speter			{
76338032Speter				usrerr("Invalid -R value");
76438032Speter				ExitStat = EX_USAGE;
76538032Speter			}
76638032Speter			break;
76738032Speter
76838032Speter		  case 't':	/* read recipients from message */
76938032Speter			GrabTo = TRUE;
77038032Speter			break;
77138032Speter
77238032Speter		  case 'U':	/* initial (user) submission */
77338032Speter			UserSubmission = TRUE;
77438032Speter			break;
77538032Speter
77638032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
77738032Speter			if (!xtextok(optarg))
77838032Speter			{
77938032Speter				usrerr("Invalid syntax in -V flag");
78038032Speter				ExitStat = EX_USAGE;
78138032Speter			}
78238032Speter			else
78338032Speter				CurEnv->e_envid = newstr(optarg);
78438032Speter			break;
78538032Speter
78638032Speter		  case 'X':	/* traffic log file */
78738032Speter			(void) drop_privileges(TRUE);
78838032Speter			TrafficLogFile = fopen(optarg, "a");
78938032Speter			if (TrafficLogFile == NULL)
79038032Speter			{
79138032Speter				syserr("cannot open %s", optarg);
79238032Speter				ExitStat = EX_CANTCREAT;
79338032Speter				break;
79438032Speter			}
79538032Speter#ifdef HASSETVBUF
79638032Speter			setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
79738032Speter#else
79838032Speter			setlinebuf(TrafficLogFile);
79938032Speter#endif
80038032Speter			break;
80138032Speter
80238032Speter			/* compatibility flags */
80338032Speter		  case 'c':	/* connect to non-local mailers */
80438032Speter		  case 'i':	/* don't let dot stop me */
80538032Speter		  case 'm':	/* send to me too */
80638032Speter		  case 'T':	/* set timeout interval */
80738032Speter		  case 'v':	/* give blow-by-blow description */
80838032Speter			setoption(j, "T", FALSE, TRUE, CurEnv);
80938032Speter			break;
81038032Speter
81138032Speter		  case 'e':	/* error message disposition */
81238032Speter		  case 'M':	/* define macro */
81338032Speter			setoption(j, optarg, FALSE, TRUE, CurEnv);
81438032Speter			break;
81538032Speter
81638032Speter		  case 's':	/* save From lines in headers */
81738032Speter			setoption('f', "T", FALSE, TRUE, CurEnv);
81838032Speter			break;
81938032Speter
82038032Speter# ifdef DBM
82138032Speter		  case 'I':	/* initialize alias DBM file */
82238032Speter			OpMode = MD_INITALIAS;
82338032Speter			break;
82438032Speter# endif /* DBM */
82538032Speter
82638032Speter# if defined(__osf__) || defined(_AIX3)
82738032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
82838032Speter			break;
82938032Speter# endif
83038032Speter# if defined(sony_news)
83138032Speter		  case 'E':
83238032Speter		  case 'J':	/* ignore flags for Japanese code conversion
83338032Speter				   impremented on Sony NEWS */
83438032Speter			break;
83538032Speter# endif
83638032Speter
83738032Speter		  default:
83838032Speter			ExitStat = EX_USAGE;
83938032Speter			finis();
84038032Speter			break;
84138032Speter		}
84238032Speter	}
84338032Speter	av += optind;
84438032Speter
84538032Speter	/*
84638032Speter	**  Do basic initialization.
84738032Speter	**	Read system control file.
84838032Speter	**	Extract special fields for local use.
84938032Speter	*/
85038032Speter
85138032Speter	/* set up ${opMode} for use in config file */
85238032Speter	{
85338032Speter		char mbuf[2];
85438032Speter
85538032Speter		mbuf[0] = OpMode;
85638032Speter		mbuf[1] = '\0';
85738032Speter		define(MID_OPMODE, newstr(mbuf), CurEnv);
85838032Speter	}
85938032Speter
86038032Speter#if XDEBUG
86138032Speter	checkfd012("before readcf");
86238032Speter#endif
86338032Speter	vendor_pre_defaults(CurEnv);
86438032Speter	readcf(getcfname(), safecf, CurEnv);
86538032Speter	ConfigFileRead = TRUE;
86638032Speter	vendor_post_defaults(CurEnv);
86738032Speter
86838032Speter	/* Enforce use of local time (null string overrides this) */
86938032Speter	if (TimeZoneSpec == NULL)
87038032Speter		unsetenv("TZ");
87138032Speter	else if (TimeZoneSpec[0] != '\0')
87238032Speter		setuserenv("TZ", TimeZoneSpec);
87338032Speter	else
87438032Speter		setuserenv("TZ", NULL);
87538032Speter	tzset();
87638032Speter
87738032Speter	/* avoid denial-of-service attacks */
87838032Speter	resetlimits();
87938032Speter
88038032Speter	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
88138032Speter	{
88238032Speter		/* drop privileges -- daemon mode done after socket/bind */
88338032Speter		(void) drop_privileges(FALSE);
88438032Speter	}
88538032Speter
88638032Speter	/*
88738032Speter	**  Find our real host name for future logging.
88838032Speter	*/
88938032Speter
89038032Speter	p = getauthinfo(STDIN_FILENO, &forged);
89138032Speter	define('_', p, CurEnv);
89238032Speter
89338032Speter	/* suppress error printing if errors mailed back or whatever */
89438032Speter	if (CurEnv->e_errormode != EM_PRINT)
89538032Speter		HoldErrs = TRUE;
89638032Speter
89738032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
89838032Speter	expand("\201m", jbuf, sizeof jbuf, CurEnv);
89938032Speter	setclass('m', jbuf);
90038032Speter
90138032Speter	/* probe interfaces and locate any additional names */
90238032Speter	if (!DontProbeInterfaces)
90338032Speter		load_if_names();
90438032Speter
90538032Speter	if (tTd(0, 1))
90638032Speter	{
90738032Speter		printf("\n============ SYSTEM IDENTITY (after readcf) ============");
90838032Speter		printf("\n      (short domain name) $w = ");
90938032Speter		xputs(macvalue('w', CurEnv));
91038032Speter		printf("\n  (canonical domain name) $j = ");
91138032Speter		xputs(macvalue('j', CurEnv));
91238032Speter		printf("\n         (subdomain name) $m = ");
91338032Speter		xputs(macvalue('m', CurEnv));
91438032Speter		printf("\n              (node name) $k = ");
91538032Speter		xputs(macvalue('k', CurEnv));
91638032Speter		printf("\n========================================================\n\n");
91738032Speter	}
91838032Speter
91938032Speter	/*
92038032Speter	**  Do more command line checking -- these are things that
92138032Speter	**  have to modify the results of reading the config file.
92238032Speter	*/
92338032Speter
92438032Speter	/* process authorization warnings from command line */
92538032Speter	if (warn_C_flag)
92638032Speter		auth_warning(CurEnv, "Processed by %s with -C %s",
92738032Speter			RealUserName, ConfFile);
92838032Speter	if (Warn_Q_option)
92938032Speter		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
93038032Speter
93138032Speter	/* check body type for legality */
93238032Speter	if (CurEnv->e_bodytype == NULL)
93338032Speter		/* nothing */ ;
93438032Speter	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
93538032Speter		SevenBitInput = TRUE;
93638032Speter	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
93738032Speter		SevenBitInput = FALSE;
93838032Speter	else
93938032Speter	{
94038032Speter		usrerr("Illegal body type %s", CurEnv->e_bodytype);
94138032Speter		CurEnv->e_bodytype = NULL;
94238032Speter	}
94338032Speter
94438032Speter	/* tweak default DSN notifications */
94538032Speter	if (DefaultNotify == 0)
94638032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
94738032Speter
94838032Speter	/* be sure we don't pick up bogus HOSTALIASES environment variable */
94938032Speter	if (queuemode && RealUid != 0)
95038032Speter		(void) unsetenv("HOSTALIASES");
95138032Speter
95238032Speter	/* check for sane configuration level */
95338032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
95438032Speter	{
95538032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
95638032Speter			ConfigLevel, Version, MAXCONFIGLEVEL);
95738032Speter	}
95838032Speter
95938032Speter	/* need MCI cache to have persistence */
96038032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
96138032Speter	{
96238032Speter		HostStatDir = NULL;
96338032Speter		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
96438032Speter	}
96538032Speter
96638032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
96738032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
96838032Speter	{
96938032Speter		SingleThreadDelivery = FALSE;
97038032Speter		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
97138032Speter	}
97238032Speter
97338032Speter	/* check for permissions */
97438032Speter	if ((OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
97538032Speter	     OpMode == MD_PURGESTAT) && RealUid != 0)
97638032Speter	{
97738032Speter		if (LogLevel > 1)
97838032Speter			sm_syslog(LOG_ALERT, NOQID,
97938032Speter				"user %d attempted to %s",
98038032Speter				RealUid,
98138032Speter				OpMode != MD_PURGESTAT ? "run daemon"
98238032Speter						       : "purge host status");
98338032Speter		usrerr("Permission denied");
98438032Speter		exit(EX_USAGE);
98538032Speter	}
98638032Speter
98738032Speter	if (MeToo)
98838032Speter		BlankEnvelope.e_flags |= EF_METOO;
98938032Speter
99038032Speter	switch (OpMode)
99138032Speter	{
99238032Speter	  case MD_TEST:
99338032Speter		/* don't have persistent host status in test mode */
99438032Speter		HostStatDir = NULL;
99538032Speter		if (Verbose == 0)
99638032Speter			Verbose = 2;
99738032Speter		CurEnv->e_errormode = EM_PRINT;
99838032Speter		HoldErrs = FALSE;
99938032Speter		break;
100038032Speter
100138032Speter	  case MD_VERIFY:
100238032Speter		CurEnv->e_errormode = EM_PRINT;
100338032Speter		HoldErrs = FALSE;
100438032Speter		/* arrange to exit cleanly on hangup signal */
100538032Speter		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
100638032Speter			setsignal(SIGHUP, intsig);
100738032Speter		break;
100838032Speter
100938032Speter	  case MD_FGDAEMON:
101038032Speter		run_in_foreground = TRUE;
101138032Speter		OpMode = MD_DAEMON;
101238032Speter		/* fall through ... */
101338032Speter
101438032Speter	  case MD_DAEMON:
101538032Speter		vendor_daemon_setup(CurEnv);
101638032Speter
101738032Speter		/* remove things that don't make sense in daemon mode */
101838032Speter		FullName = NULL;
101938032Speter		GrabTo = FALSE;
102038032Speter
102138032Speter		/* arrange to restart on hangup signal */
102238032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
102338032Speter			sm_syslog(LOG_WARNING, NOQID,
102438032Speter				"daemon invoked without full pathname; kill -1 won't work");
102538032Speter		setsignal(SIGHUP, sighup);
102638032Speter
102738032Speter		/* workaround: can't seem to release the signal in the parent */
102838032Speter		releasesignal(SIGHUP);
102938032Speter		break;
103038032Speter
103138032Speter	  case MD_INITALIAS:
103238032Speter		Verbose = 2;
103338032Speter		CurEnv->e_errormode = EM_PRINT;
103438032Speter		HoldErrs = FALSE;
103538032Speter		/* fall through... */
103638032Speter
103738032Speter	  case MD_PRINT:
103838032Speter		/* to handle sendmail -bp -qSfoobar properly */
103938032Speter		queuemode = FALSE;
104038032Speter		/* fall through... */
104138032Speter
104238032Speter	  default:
104338032Speter		/* arrange to exit cleanly on hangup signal */
104438032Speter		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
104538032Speter			setsignal(SIGHUP, intsig);
104638032Speter		break;
104738032Speter	}
104838032Speter
104938032Speter	/* special considerations for FullName */
105038032Speter	if (FullName != NULL)
105138032Speter	{
105238032Speter		char *full = NULL;
105338032Speter		extern bool rfc822_string __P((char *));
105438032Speter
105538032Speter		/* full names can't have newlines */
105638032Speter		if (strchr(FullName, '\n') != NULL)
105738032Speter		{
105838032Speter			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
105938032Speter		}
106038032Speter		/* check for characters that may have to be quoted */
106138032Speter		if (!rfc822_string(FullName))
106238032Speter		{
106338032Speter			extern char *addquotes __P((char *));
106438032Speter
106538032Speter			/*
106638032Speter			**  Quote a full name with special characters
106738032Speter			**  as a comment so crackaddr() doesn't destroy
106838032Speter			**  the name portion of the address.
106938032Speter			*/
107038032Speter			FullName = addquotes(FullName);
107138032Speter			if (full != NULL)
107238032Speter				free(full);
107338032Speter		}
107438032Speter	}
107538032Speter
107638032Speter	/* do heuristic mode adjustment */
107738032Speter	if (Verbose)
107838032Speter	{
107938032Speter		/* turn off noconnect option */
108038032Speter		setoption('c', "F", TRUE, FALSE, CurEnv);
108138032Speter
108238032Speter		/* turn on interactive delivery */
108338032Speter		setoption('d', "", TRUE, FALSE, CurEnv);
108438032Speter	}
108538032Speter
108638032Speter	/* check for out of date configuration level */
108738032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
108838032Speter	{
108938032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
109038032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
109138032Speter	}
109238032Speter
109338032Speter	if (ConfigLevel < 3)
109438032Speter	{
109538032Speter		UseErrorsTo = TRUE;
109638032Speter	}
109738032Speter
109838032Speter	/* set options that were previous macros */
109938032Speter	if (SmtpGreeting == NULL)
110038032Speter	{
110138032Speter		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
110238032Speter			SmtpGreeting = newstr(p);
110338032Speter		else
110438032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
110538032Speter	}
110638032Speter	if (UnixFromLine == NULL)
110738032Speter	{
110838032Speter		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
110938032Speter			UnixFromLine = newstr(p);
111038032Speter		else
111138032Speter			UnixFromLine = "From \201g  \201d";
111238032Speter	}
111338032Speter
111438032Speter	/* our name for SMTP codes */
111538032Speter	expand("\201j", jbuf, sizeof jbuf, CurEnv);
111638032Speter	MyHostName = jbuf;
111738032Speter	if (strchr(jbuf, '.') == NULL)
111838032Speter		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
111938032Speter			jbuf);
112038032Speter
112138032Speter	/* make certain that this name is part of the $=w class */
112238032Speter	setclass('w', MyHostName);
112338032Speter
112438032Speter	/* the indices of built-in mailers */
112538032Speter	st = stab("local", ST_MAILER, ST_FIND);
112638032Speter	if (st != NULL)
112738032Speter		LocalMailer = st->s_mailer;
112838032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
112938032Speter		syserr("No local mailer defined");
113038032Speter
113138032Speter	st = stab("prog", ST_MAILER, ST_FIND);
113238032Speter	if (st == NULL)
113338032Speter		syserr("No prog mailer defined");
113438032Speter	else
113538032Speter	{
113638032Speter		ProgMailer = st->s_mailer;
113738032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
113838032Speter	}
113938032Speter
114038032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
114138032Speter	if (st == NULL)
114238032Speter		syserr("No *file* mailer defined");
114338032Speter	else
114438032Speter	{
114538032Speter		FileMailer = st->s_mailer;
114638032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
114738032Speter	}
114838032Speter
114938032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
115038032Speter	if (st == NULL)
115138032Speter		syserr("No *include* mailer defined");
115238032Speter	else
115338032Speter		InclMailer = st->s_mailer;
115438032Speter
115538032Speter	if (ConfigLevel < 6)
115638032Speter	{
115738032Speter		/* heuristic tweaking of local mailer for back compat */
115838032Speter		if (LocalMailer != NULL)
115938032Speter		{
116038032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
116138032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
116238032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
116338032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
116438032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
116538032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
116638032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
116738032Speter		}
116838032Speter		if (ProgMailer != NULL)
116938032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
117038032Speter		if (FileMailer != NULL)
117138032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
117238032Speter	}
117338032Speter	if (ConfigLevel < 7)
117438032Speter	{
117538032Speter		if (LocalMailer != NULL)
117638032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
117738032Speter		if (ProgMailer != NULL)
117838032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
117938032Speter		if (FileMailer != NULL)
118038032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
118138032Speter	}
118238032Speter
118338032Speter	/* MIME Content-Types that cannot be transfer encoded */
118438032Speter	setclass('n', "multipart/signed");
118538032Speter
118638032Speter	/* MIME message/xxx subtypes that can be treated as messages */
118738032Speter	setclass('s', "rfc822");
118838032Speter
118938032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
119038032Speter	setclass('e', "7bit");
119138032Speter	setclass('e', "8bit");
119238032Speter	setclass('e', "binary");
119338032Speter
119438032Speter#ifdef USE_B_CLASS
119538032Speter	/* MIME Content-Types that should be treated as binary */
119638032Speter	setclass('b', "image");
119738032Speter	setclass('b', "audio");
119838032Speter	setclass('b', "video");
119938032Speter	setclass('b', "application/octet-stream");
120038032Speter#endif
120138032Speter
120238032Speter	/* operate in queue directory */
120338032Speter	if (QueueDir == NULL)
120438032Speter	{
120538032Speter		if (OpMode != MD_TEST)
120638032Speter		{
120738032Speter			syserr("QueueDirectory (Q) option must be set");
120838032Speter			ExitStat = EX_CONFIG;
120938032Speter		}
121038032Speter	}
121138032Speter	else
121238032Speter	{
121338032Speter		/* test path to get warning messages */
121438032Speter		(void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
121538032Speter		if (OpMode != MD_TEST && chdir(QueueDir) < 0)
121638032Speter		{
121738032Speter			syserr("cannot chdir(%s)", QueueDir);
121838032Speter			ExitStat = EX_CONFIG;
121938032Speter		}
122038032Speter	}
122138032Speter
122238032Speter	/* check host status directory for validity */
122338032Speter	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
122438032Speter	{
122538032Speter		/* cannot use this value */
122638032Speter		if (tTd(0, 2))
122738032Speter			printf("Cannot use HostStatusDirectory = %s: %s\n",
122838032Speter				HostStatDir, errstring(errno));
122938032Speter		HostStatDir = NULL;
123038032Speter	}
123138032Speter
123238032Speter# if QUEUE
123338032Speter	if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
123438032Speter	{
123538032Speter		struct stat stbuf;
123638032Speter
123738032Speter		/* check to see if we own the queue directory */
123838032Speter		if (stat(".", &stbuf) < 0)
123938032Speter			syserr("main: cannot stat %s", QueueDir);
124038032Speter		if (stbuf.st_uid != RealUid)
124138032Speter		{
124238032Speter			/* nope, really a botch */
124338032Speter			usrerr("You do not have permission to process the queue");
124438032Speter			exit (EX_NOPERM);
124538032Speter		}
124638032Speter	}
124738032Speter# endif /* QUEUE */
124838032Speter
124938032Speter	/* if we've had errors so far, exit now */
125038032Speter	if (ExitStat != EX_OK && OpMode != MD_TEST)
125138032Speter	{
125238032Speter		endpwent();
125338032Speter		setuid(RealUid);
125438032Speter		exit(ExitStat);
125538032Speter	}
125638032Speter
125738032Speter#if XDEBUG
125838032Speter	checkfd012("before main() initmaps");
125938032Speter#endif
126038032Speter
126138032Speter	/*
126238032Speter	**  Do operation-mode-dependent initialization.
126338032Speter	*/
126438032Speter
126538032Speter	switch (OpMode)
126638032Speter	{
126738032Speter	  case MD_PRINT:
126838032Speter		/* print the queue */
126938032Speter#if QUEUE
127038032Speter		dropenvelope(CurEnv, TRUE);
127138032Speter		printqueue();
127238032Speter		endpwent();
127338032Speter		setuid(RealUid);
127438032Speter		exit(EX_OK);
127538032Speter#else /* QUEUE */
127638032Speter		usrerr("No queue to print");
127738032Speter		finis();
127838032Speter#endif /* QUEUE */
127938032Speter
128038032Speter	  case MD_HOSTSTAT:
128138032Speter		mci_traverse_persistent(mci_print_persistent, NULL);
128238032Speter		exit(EX_OK);
128338032Speter	    	break;
128438032Speter
128538032Speter	  case MD_PURGESTAT:
128638032Speter		mci_traverse_persistent(mci_purge_persistent, NULL);
128738032Speter		exit(EX_OK);
128838032Speter	    	break;
128938032Speter
129038032Speter	  case MD_INITALIAS:
129138032Speter		/* initialize alias database */
129238032Speter		initmaps(TRUE, CurEnv);
129338032Speter		endpwent();
129438032Speter		setuid(RealUid);
129538032Speter		exit(ExitStat);
129638032Speter
129738032Speter	  case MD_SMTP:
129838032Speter	  case MD_DAEMON:
129938032Speter		/* reset DSN parameters */
130038032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
130138032Speter		CurEnv->e_envid = NULL;
130238032Speter		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
130338032Speter
130438032Speter		/* don't open alias database -- done in srvrsmtp */
130538032Speter		break;
130638032Speter
130738032Speter	  default:
130838032Speter		/* open the alias database */
130938032Speter		initmaps(FALSE, CurEnv);
131038032Speter		break;
131138032Speter	}
131238032Speter
131338032Speter	if (tTd(0, 15))
131438032Speter	{
131538032Speter		extern void printrules __P((void));
131638032Speter
131738032Speter		/* print configuration table (or at least part of it) */
131838032Speter		if (tTd(0, 90))
131938032Speter			printrules();
132038032Speter		for (i = 0; i < MAXMAILERS; i++)
132138032Speter		{
132238032Speter			if (Mailer[i] != NULL)
132338032Speter				printmailer(Mailer[i]);
132438032Speter		}
132538032Speter	}
132638032Speter
132738032Speter	/*
132838032Speter	**  Switch to the main envelope.
132938032Speter	*/
133038032Speter
133138032Speter	CurEnv = newenvelope(&MainEnvelope, CurEnv);
133238032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
133338032Speter
133438032Speter	/*
133538032Speter	**  If test mode, read addresses from stdin and process.
133638032Speter	*/
133738032Speter
133838032Speter	if (OpMode == MD_TEST)
133938032Speter	{
134038032Speter		char buf[MAXLINE];
134138032Speter		SIGFUNC_DECL intindebug __P((int));
134238032Speter
134338032Speter		if (isatty(fileno(stdin)))
134438032Speter			Verbose = 2;
134538032Speter
134638032Speter		if (Verbose)
134738032Speter		{
134838032Speter			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
134938032Speter			printf("Enter <ruleset> <address>\n");
135038032Speter		}
135138032Speter		if (setjmp(TopFrame) > 0)
135238032Speter			printf("\n");
135338032Speter		(void) setsignal(SIGINT, intindebug);
135438032Speter		for (;;)
135538032Speter		{
135638032Speter			extern void testmodeline __P((char *, ENVELOPE *));
135738032Speter
135838032Speter			if (Verbose == 2)
135938032Speter				printf("> ");
136038032Speter			(void) fflush(stdout);
136138032Speter			if (fgets(buf, sizeof buf, stdin) == NULL)
136238032Speter				finis();
136338032Speter			p = strchr(buf, '\n');
136438032Speter			if (p != NULL)
136538032Speter				*p = '\0';
136638032Speter			if (Verbose < 2)
136738032Speter				printf("> %s\n", buf);
136838032Speter			testmodeline(buf, CurEnv);
136938032Speter		}
137038032Speter	}
137138032Speter
137238032Speter# if QUEUE
137338032Speter	/*
137438032Speter	**  If collecting stuff from the queue, go start doing that.
137538032Speter	*/
137638032Speter
137738032Speter	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
137838032Speter	{
137938032Speter		(void) runqueue(FALSE, Verbose);
138038032Speter		finis();
138138032Speter	}
138238032Speter# endif /* QUEUE */
138338032Speter
138438032Speter	/*
138538032Speter	**  If a daemon, wait for a request.
138638032Speter	**	getrequests will always return in a child.
138738032Speter	**	If we should also be processing the queue, start
138838032Speter	**		doing it in background.
138938032Speter	**	We check for any errors that might have happened
139038032Speter	**		during startup.
139138032Speter	*/
139238032Speter
139338032Speter	if (OpMode == MD_DAEMON || QueueIntvl != 0)
139438032Speter	{
139538032Speter		char dtype[200];
139638032Speter		extern void getrequests __P((ENVELOPE *));
139738032Speter
139838032Speter		if (!run_in_foreground && !tTd(99, 100))
139938032Speter		{
140038032Speter			/* put us in background */
140138032Speter			i = fork();
140238032Speter			if (i < 0)
140338032Speter				syserr("daemon: cannot fork");
140438032Speter			if (i != 0)
140538032Speter				exit(EX_OK);
140638032Speter
140738032Speter			/* disconnect from our controlling tty */
140838032Speter			disconnect(2, CurEnv);
140938032Speter		}
141038032Speter
141138032Speter		dtype[0] = '\0';
141238032Speter		if (OpMode == MD_DAEMON)
141338032Speter			strcat(dtype, "+SMTP");
141438032Speter		if (QueueIntvl != 0)
141538032Speter		{
141638032Speter			strcat(dtype, "+queueing@");
141738032Speter			strcat(dtype, pintvl(QueueIntvl, TRUE));
141838032Speter		}
141938032Speter		if (tTd(0, 1))
142038032Speter			strcat(dtype, "+debugging");
142138032Speter
142238032Speter		sm_syslog(LOG_INFO, NOQID,
142338032Speter			"starting daemon (%s): %s", Version, dtype + 1);
142438032Speter#ifdef XLA
142538032Speter		xla_create_file();
142638032Speter#endif
142738032Speter
142838032Speter# if QUEUE
142938032Speter		if (queuemode)
143038032Speter		{
143138032Speter			(void) runqueue(TRUE, FALSE);
143238032Speter			if (OpMode != MD_DAEMON)
143338032Speter			{
143438032Speter				for (;;)
143538032Speter				{
143638032Speter					pause();
143738032Speter					if (DoQueueRun)
143838032Speter						(void) runqueue(TRUE, FALSE);
143938032Speter				}
144038032Speter			}
144138032Speter		}
144238032Speter# endif /* QUEUE */
144338032Speter		dropenvelope(CurEnv, TRUE);
144438032Speter
144538032Speter#if DAEMON
144638032Speter		getrequests(CurEnv);
144738032Speter
144838032Speter		/* drop privileges */
144938032Speter		(void) drop_privileges(FALSE);
145038032Speter
145138032Speter		/* at this point we are in a child: reset state */
145238032Speter		(void) newenvelope(CurEnv, CurEnv);
145338032Speter
145438032Speter		/*
145538032Speter		**  Get authentication data
145638032Speter		*/
145738032Speter
145838032Speter		p = getauthinfo(fileno(InChannel), &forged);
145938032Speter		define('_', p, &BlankEnvelope);
146038032Speter#endif /* DAEMON */
146138032Speter	}
146238032Speter
146338032Speter# if SMTP
146438032Speter	/*
146538032Speter	**  If running SMTP protocol, start collecting and executing
146638032Speter	**  commands.  This will never return.
146738032Speter	*/
146838032Speter
146938032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
147038032Speter	{
147138032Speter		char pbuf[20];
147238032Speter		extern void smtp __P((char *, ENVELOPE *));
147338032Speter
147438032Speter		/*
147538032Speter		**  Save some macros for check_* rulesets.
147638032Speter		*/
147738032Speter
147838032Speter		if (forged)
147938032Speter		{
148038032Speter			char ipbuf[103];
148138032Speter
148238032Speter			snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
148338032Speter				 inet_ntoa(RealHostAddr.sin.sin_addr));
148438032Speter
148538032Speter			define(macid("{client_name}", NULL),
148638032Speter			       newstr(ipbuf), &BlankEnvelope);
148738032Speter		}
148838032Speter		else
148938032Speter			define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
149038032Speter		define(macid("{client_addr}", NULL),
149138032Speter		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
149238032Speter		if (RealHostAddr.sa.sa_family == AF_INET)
149338032Speter			snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
149438032Speter		else
149538032Speter			snprintf(pbuf, sizeof pbuf, "0");
149638032Speter		define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
149738032Speter
149838032Speter		if (OpMode == MD_DAEMON)
149938032Speter		{
150038032Speter			/* validate the connection */
150138032Speter			HoldErrs = TRUE;
150238032Speter			nullserver = validate_connection(&RealHostAddr,
150338032Speter							 RealHostName, CurEnv);
150438032Speter			HoldErrs = FALSE;
150538032Speter		}
150638032Speter		smtp(nullserver, CurEnv);
150738032Speter	}
150838032Speter# endif /* SMTP */
150938032Speter
151038032Speter	clearenvelope(CurEnv, FALSE);
151138032Speter	if (OpMode == MD_VERIFY)
151238032Speter	{
151338032Speter		CurEnv->e_sendmode = SM_VERIFY;
151438032Speter		PostMasterCopy = NULL;
151538032Speter	}
151638032Speter	else
151738032Speter	{
151838032Speter		/* interactive -- all errors are global */
151938032Speter		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
152038032Speter	}
152138032Speter
152238032Speter	/*
152338032Speter	**  Do basic system initialization and set the sender
152438032Speter	*/
152538032Speter
152638032Speter	initsys(CurEnv);
152738032Speter	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
152838032Speter		auth_warning(CurEnv, "%s set sender to %s using -%c",
152938032Speter			RealUserName, from, warn_f_flag);
153038032Speter	setsender(from, CurEnv, NULL, '\0', FALSE);
153138032Speter	if (macvalue('s', CurEnv) == NULL)
153238032Speter		define('s', RealHostName, CurEnv);
153338032Speter
153438032Speter	if (*av == NULL && !GrabTo)
153538032Speter	{
153638032Speter		CurEnv->e_flags |= EF_GLOBALERRS;
153738032Speter		usrerr("Recipient names must be specified");
153838032Speter
153938032Speter		/* collect body for UUCP return */
154038032Speter		if (OpMode != MD_VERIFY)
154138032Speter			collect(InChannel, FALSE, NULL, CurEnv);
154238032Speter		finis();
154338032Speter	}
154438032Speter
154538032Speter	/*
154638032Speter	**  Scan argv and deliver the message to everyone.
154738032Speter	*/
154838032Speter
154938032Speter	sendtoargv(av, CurEnv);
155038032Speter
155138032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
155238032Speter	if (Errors > 0 && ExitStat == EX_OK)
155338032Speter		ExitStat = EX_USAGE;
155438032Speter
155538032Speter#if _FFR_FIX_DASHT
155638032Speter	/*
155738032Speter	**  If using -t, force not sending to argv recipients, even
155838032Speter	**  if they are mentioned in the headers.
155938032Speter	*/
156038032Speter
156138032Speter	if (GrabTo)
156238032Speter	{
156338032Speter		ADDRESS *q;
156438032Speter
156538032Speter		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
156638032Speter			q->q_flags |= QDONTSEND;
156738032Speter	}
156838032Speter#endif
156938032Speter
157038032Speter	/*
157138032Speter	**  Read the input mail.
157238032Speter	*/
157338032Speter
157438032Speter	CurEnv->e_to = NULL;
157538032Speter	if (OpMode != MD_VERIFY || GrabTo)
157638032Speter	{
157738032Speter		long savedflags = CurEnv->e_flags & EF_FATALERRS;
157838032Speter
157938032Speter		CurEnv->e_flags |= EF_GLOBALERRS;
158038032Speter		CurEnv->e_flags &= ~EF_FATALERRS;
158138032Speter		collect(InChannel, FALSE, NULL, CurEnv);
158238032Speter
158338032Speter		/* bail out if message too large */
158438032Speter		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
158538032Speter		{
158638032Speter			finis();
158738032Speter			/*NOTREACHED*/
158838032Speter			return -1;
158938032Speter		}
159038032Speter		CurEnv->e_flags |= savedflags;
159138032Speter	}
159238032Speter	errno = 0;
159338032Speter
159438032Speter	if (tTd(1, 1))
159538032Speter		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
159638032Speter
159738032Speter	/*
159838032Speter	**  Actually send everything.
159938032Speter	**	If verifying, just ack.
160038032Speter	*/
160138032Speter
160238032Speter	CurEnv->e_from.q_flags |= QDONTSEND;
160338032Speter	if (tTd(1, 5))
160438032Speter	{
160538032Speter		printf("main: QDONTSEND ");
160638032Speter		printaddr(&CurEnv->e_from, FALSE);
160738032Speter	}
160838032Speter	CurEnv->e_to = NULL;
160938032Speter	CurrentLA = getla();
161038032Speter	GrabTo = FALSE;
161138032Speter	sendall(CurEnv, SM_DEFAULT);
161238032Speter
161338032Speter	/*
161438032Speter	**  All done.
161538032Speter	**	Don't send return error message if in VERIFY mode.
161638032Speter	*/
161738032Speter
161838032Speter	finis();
161938032Speter	/*NOTREACHED*/
162038032Speter	return -1;
162138032Speter}
162238032Speter
162338032Speter
162438032Speter/* ARGSUSED */
162538032SpeterSIGFUNC_DECL
162638032Speterintindebug(sig)
162738032Speter	int sig;
162838032Speter{
162938032Speter	longjmp(TopFrame, 1);
163038032Speter	return SIGFUNC_RETURN;
163138032Speter}
163238032Speter
163338032Speter
163438032Speter/*
163538032Speter**  FINIS -- Clean up and exit.
163638032Speter**
163738032Speter**	Parameters:
163838032Speter**		none
163938032Speter**
164038032Speter**	Returns:
164138032Speter**		never
164238032Speter**
164338032Speter**	Side Effects:
164438032Speter**		exits sendmail
164538032Speter*/
164638032Speter
164738032Spetervoid
164838032Speterfinis()
164938032Speter{
165038032Speter	if (tTd(2, 1))
165138032Speter	{
165238032Speter		extern void printenvflags __P((ENVELOPE *));
165338032Speter
165438032Speter		printf("\n====finis: stat %d e_id=%s e_flags=",
165538032Speter			ExitStat,
165638032Speter			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
165738032Speter		printenvflags(CurEnv);
165838032Speter	}
165938032Speter	if (tTd(2, 9))
166038032Speter		printopenfds(FALSE);
166138032Speter
166238032Speter	/* if we fail in finis(), just exit */
166338032Speter	if (setjmp(TopFrame) != 0)
166438032Speter	{
166538032Speter		/* failed -- just give it up */
166638032Speter		goto forceexit;
166738032Speter	}
166838032Speter
166938032Speter	/* clean up temp files */
167038032Speter	CurEnv->e_to = NULL;
167138032Speter	if (CurEnv->e_id != NULL)
167238032Speter		dropenvelope(CurEnv, TRUE);
167338032Speter
167438032Speter	/* flush any cached connections */
167538032Speter	mci_flush(TRUE, NULL);
167638032Speter
167738032Speter# ifdef XLA
167838032Speter	/* clean up extended load average stuff */
167938032Speter	xla_all_end();
168038032Speter# endif
168138032Speter
168238032Speter	/* and exit */
168338032Speter  forceexit:
168438032Speter	if (LogLevel > 78)
168538032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
168638032Speter			"finis, pid=%d",
168738032Speter			getpid());
168838032Speter	if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
168938032Speter		ExitStat = EX_OK;
169038032Speter
169138032Speter	/* reset uid for process accounting */
169238032Speter	endpwent();
169338032Speter	setuid(RealUid);
169438032Speter
169538032Speter	exit(ExitStat);
169638032Speter}
169738032Speter/*
169838032Speter**  INTSIG -- clean up on interrupt
169938032Speter**
170038032Speter**	This just arranges to exit.  It pessimises in that it
170138032Speter**	may resend a message.
170238032Speter**
170338032Speter**	Parameters:
170438032Speter**		none.
170538032Speter**
170638032Speter**	Returns:
170738032Speter**		none.
170838032Speter**
170938032Speter**	Side Effects:
171038032Speter**		Unlocks the current job.
171138032Speter*/
171238032Speter
171338032Speter/* ARGSUSED */
171438032SpeterSIGFUNC_DECL
171538032Speterintsig(sig)
171638032Speter	int sig;
171738032Speter{
171838032Speter	if (LogLevel > 79)
171938032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
172038032Speter	FileName = NULL;
172138032Speter	unlockqueue(CurEnv);
172238032Speter#ifdef XLA
172338032Speter	xla_all_end();
172438032Speter#endif
172538032Speter
172638032Speter	/* reset uid for process accounting */
172738032Speter	endpwent();
172838032Speter	setuid(RealUid);
172938032Speter
173038032Speter	exit(EX_OK);
173138032Speter}
173238032Speter/*
173338032Speter**  INITMACROS -- initialize the macro system
173438032Speter**
173538032Speter**	This just involves defining some macros that are actually
173638032Speter**	used internally as metasymbols to be themselves.
173738032Speter**
173838032Speter**	Parameters:
173938032Speter**		none.
174038032Speter**
174138032Speter**	Returns:
174238032Speter**		none.
174338032Speter**
174438032Speter**	Side Effects:
174538032Speter**		initializes several macros to be themselves.
174638032Speter*/
174738032Speter
174838032Speterstruct metamac	MetaMacros[] =
174938032Speter{
175038032Speter	/* LHS pattern matching characters */
175138032Speter	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
175238032Speter	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
175338032Speter
175438032Speter	/* these are RHS metasymbols */
175538032Speter	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
175638032Speter	{ '>', CALLSUBR },
175738032Speter
175838032Speter	/* the conditional operations */
175938032Speter	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
176038032Speter
176138032Speter	/* the hostname lookup characters */
176238032Speter	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
176338032Speter	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
176438032Speter
176538032Speter	/* miscellaneous control characters */
176638032Speter	{ '&', MACRODEXPAND },
176738032Speter
176838032Speter	{ '\0' }
176938032Speter};
177038032Speter
177138032Speter#define MACBINDING(name, mid) \
177238032Speter		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
177338032Speter		MacroName[mid] = name;
177438032Speter
177538032Spetervoid
177638032Speterinitmacros(e)
177738032Speter	register ENVELOPE *e;
177838032Speter{
177938032Speter	register struct metamac *m;
178038032Speter	register int c;
178138032Speter	char buf[5];
178238032Speter	extern char *MacroName[256];
178338032Speter
178438032Speter	for (m = MetaMacros; m->metaname != '\0'; m++)
178538032Speter	{
178638032Speter		buf[0] = m->metaval;
178738032Speter		buf[1] = '\0';
178838032Speter		define(m->metaname, newstr(buf), e);
178938032Speter	}
179038032Speter	buf[0] = MATCHREPL;
179138032Speter	buf[2] = '\0';
179238032Speter	for (c = '0'; c <= '9'; c++)
179338032Speter	{
179438032Speter		buf[1] = c;
179538032Speter		define(c, newstr(buf), e);
179638032Speter	}
179738032Speter
179838032Speter	/* set defaults for some macros sendmail will use later */
179938032Speter	define('n', "MAILER-DAEMON", e);
180038032Speter
180138032Speter	/* set up external names for some internal macros */
180238032Speter	MACBINDING("opMode", MID_OPMODE);
180338032Speter	/*XXX should probably add equivalents for all short macros here XXX*/
180438032Speter}
180538032Speter/*
180638032Speter**  DISCONNECT -- remove our connection with any foreground process
180738032Speter**
180838032Speter**	Parameters:
180938032Speter**		droplev -- how "deeply" we should drop the line.
181038032Speter**			0 -- ignore signals, mail back errors, make sure
181138032Speter**			     output goes to stdout.
181238032Speter**			1 -- also, make stdout go to transcript.
181338032Speter**			2 -- also, disconnect from controlling terminal
181438032Speter**			     (only for daemon mode).
181538032Speter**		e -- the current envelope.
181638032Speter**
181738032Speter**	Returns:
181838032Speter**		none
181938032Speter**
182038032Speter**	Side Effects:
182138032Speter**		Trys to insure that we are immune to vagaries of
182238032Speter**		the controlling tty.
182338032Speter*/
182438032Speter
182538032Spetervoid
182638032Speterdisconnect(droplev, e)
182738032Speter	int droplev;
182838032Speter	register ENVELOPE *e;
182938032Speter{
183038032Speter	int fd;
183138032Speter
183238032Speter	if (tTd(52, 1))
183338032Speter		printf("disconnect: In %d Out %d, e=%lx\n",
183438032Speter			fileno(InChannel), fileno(OutChannel), (u_long) e);
183538032Speter	if (tTd(52, 100))
183638032Speter	{
183738032Speter		printf("don't\n");
183838032Speter		return;
183938032Speter	}
184038032Speter	if (LogLevel > 93)
184138032Speter		sm_syslog(LOG_DEBUG, e->e_id,
184238032Speter			"disconnect level %d",
184338032Speter			droplev);
184438032Speter
184538032Speter	/* be sure we don't get nasty signals */
184638032Speter	(void) setsignal(SIGINT, SIG_IGN);
184738032Speter	(void) setsignal(SIGQUIT, SIG_IGN);
184838032Speter
184938032Speter	/* we can't communicate with our caller, so.... */
185038032Speter	HoldErrs = TRUE;
185138032Speter	CurEnv->e_errormode = EM_MAIL;
185238032Speter	Verbose = 0;
185338032Speter	DisConnected = TRUE;
185438032Speter
185538032Speter	/* all input from /dev/null */
185638032Speter	if (InChannel != stdin)
185738032Speter	{
185838032Speter		(void) fclose(InChannel);
185938032Speter		InChannel = stdin;
186038032Speter	}
186138032Speter	if (freopen("/dev/null", "r", stdin) == NULL)
186238032Speter		sm_syslog(LOG_ERR, e->e_id,
186338032Speter			  "disconnect: freopen(\"/dev/null\") failed: %s",
186438032Speter			  errstring(errno));
186538032Speter
186638032Speter	/* output to the transcript */
186738032Speter	if (OutChannel != stdout)
186838032Speter	{
186938032Speter		(void) fclose(OutChannel);
187038032Speter		OutChannel = stdout;
187138032Speter	}
187238032Speter	if (droplev > 0)
187338032Speter	{
187438032Speter		if (e->e_xfp == NULL)
187538032Speter		{
187638032Speter			fd = open("/dev/null", O_WRONLY, 0666);
187738032Speter			if (fd == -1)
187838032Speter				sm_syslog(LOG_ERR, e->e_id,
187938032Speter					  "disconnect: open(\"/dev/null\") failed: %s",
188038032Speter					  errstring(errno));
188138032Speter		}
188238032Speter		else
188338032Speter		{
188438032Speter			fd = fileno(e->e_xfp);
188538032Speter			if (fd == -1)
188638032Speter				sm_syslog(LOG_ERR, e->e_id,
188738032Speter					  "disconnect: fileno(e->e_xfp) failed: %s",
188838032Speter					  errstring(errno));
188938032Speter		}
189038032Speter		(void) fflush(stdout);
189138032Speter		dup2(fd, STDOUT_FILENO);
189238032Speter		dup2(fd, STDERR_FILENO);
189338032Speter		if (e->e_xfp == NULL)
189438032Speter			close(fd);
189538032Speter	}
189638032Speter
189738032Speter	/* drop our controlling TTY completely if possible */
189838032Speter	if (droplev > 1)
189938032Speter	{
190038032Speter		(void) setsid();
190138032Speter		errno = 0;
190238032Speter	}
190338032Speter
190438032Speter#if XDEBUG
190538032Speter	checkfd012("disconnect");
190638032Speter#endif
190738032Speter
190838032Speter	if (LogLevel > 71)
190938032Speter		sm_syslog(LOG_DEBUG, e->e_id,
191038032Speter			"in background, pid=%d",
191138032Speter			getpid());
191238032Speter
191338032Speter	errno = 0;
191438032Speter}
191538032Speter
191638032Speterstatic void
191738032Speterobsolete(argv)
191838032Speter	char *argv[];
191938032Speter{
192038032Speter	register char *ap;
192138032Speter	register char *op;
192238032Speter
192338032Speter	while ((ap = *++argv) != NULL)
192438032Speter	{
192538032Speter		/* Return if "--" or not an option of any form. */
192638032Speter		if (ap[0] != '-' || ap[1] == '-')
192738032Speter			return;
192838032Speter
192938032Speter		/* skip over options that do have a value */
193038032Speter		op = strchr(OPTIONS, ap[1]);
193138032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
193238032Speter		    ap[1] != 'd' &&
193338032Speter#if defined(sony_news)
193438032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
193538032Speter#endif
193638032Speter		    argv[1] != NULL && argv[1][0] != '-')
193738032Speter		{
193838032Speter			argv++;
193938032Speter			continue;
194038032Speter		}
194138032Speter
194238032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
194338032Speter#define	__DEFPATH	"sendmail.cf"
194438032Speter		if (ap[1] == 'C' && ap[2] == '\0')
194538032Speter		{
194638032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
194738032Speter			argv[0][0] = '-';
194838032Speter			argv[0][1] = 'C';
194938032Speter			(void)strcpy(&argv[0][2], __DEFPATH);
195038032Speter		}
195138032Speter
195238032Speter		/* If -q doesn't have an argument, run it once. */
195338032Speter		if (ap[1] == 'q' && ap[2] == '\0')
195438032Speter			*argv = "-q0";
195538032Speter
195638032Speter		/* if -d doesn't have an argument, use 0-99.1 */
195738032Speter		if (ap[1] == 'd' && ap[2] == '\0')
195838032Speter			*argv = "-d0-99.1";
195938032Speter
196038032Speter# if defined(sony_news)
196138032Speter		/* if -E doesn't have an argument, use -EC */
196238032Speter		if (ap[1] == 'E' && ap[2] == '\0')
196338032Speter			*argv = "-EC";
196438032Speter
196538032Speter		/* if -J doesn't have an argument, use -JJ */
196638032Speter		if (ap[1] == 'J' && ap[2] == '\0')
196738032Speter			*argv = "-JJ";
196838032Speter# endif
196938032Speter	}
197038032Speter}
197138032Speter/*
197238032Speter**  AUTH_WARNING -- specify authorization warning
197338032Speter**
197438032Speter**	Parameters:
197538032Speter**		e -- the current envelope.
197638032Speter**		msg -- the text of the message.
197738032Speter**		args -- arguments to the message.
197838032Speter**
197938032Speter**	Returns:
198038032Speter**		none.
198138032Speter*/
198238032Speter
198338032Spetervoid
198438032Speter#ifdef __STDC__
198538032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
198638032Speter#else
198738032Speterauth_warning(e, msg, va_alist)
198838032Speter	register ENVELOPE *e;
198938032Speter	const char *msg;
199038032Speter	va_dcl
199138032Speter#endif
199238032Speter{
199338032Speter	char buf[MAXLINE];
199438032Speter	VA_LOCAL_DECL
199538032Speter
199638032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
199738032Speter	{
199838032Speter		register char *p;
199938032Speter		static char hostbuf[48];
200038032Speter		extern struct hostent *myhostname __P((char *, int));
200138032Speter
200238032Speter		if (hostbuf[0] == '\0')
200338032Speter			(void) myhostname(hostbuf, sizeof hostbuf);
200438032Speter
200538032Speter		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
200638032Speter		p = &buf[strlen(buf)];
200738032Speter		VA_START(msg);
200838032Speter		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
200938032Speter		VA_END;
201038032Speter		addheader("X-Authentication-Warning", buf, &e->e_header);
201138032Speter		if (LogLevel > 3)
201238032Speter			sm_syslog(LOG_INFO, e->e_id,
201338032Speter				"Authentication-Warning: %.400s",
201438032Speter				buf);
201538032Speter	}
201638032Speter}
201738032Speter/*
201838032Speter**  GETEXTENV -- get from external environment
201938032Speter**
202038032Speter**	Parameters:
202138032Speter**		envar -- the name of the variable to retrieve
202238032Speter**
202338032Speter**	Returns:
202438032Speter**		The value, if any.
202538032Speter*/
202638032Speter
202738032Speterchar *
202838032Spetergetextenv(envar)
202938032Speter	const char *envar;
203038032Speter{
203138032Speter	char **envp;
203238032Speter	int l;
203338032Speter
203438032Speter	l = strlen(envar);
203538032Speter	for (envp = ExternalEnviron; *envp != NULL; envp++)
203638032Speter	{
203738032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
203838032Speter			return &(*envp)[l + 1];
203938032Speter	}
204038032Speter	return NULL;
204138032Speter}
204238032Speter/*
204338032Speter**  SETUSERENV -- set an environment in the propogated environment
204438032Speter**
204538032Speter**	Parameters:
204638032Speter**		envar -- the name of the environment variable.
204738032Speter**		value -- the value to which it should be set.  If
204838032Speter**			null, this is extracted from the incoming
204938032Speter**			environment.  If that is not set, the call
205038032Speter**			to setuserenv is ignored.
205138032Speter**
205238032Speter**	Returns:
205338032Speter**		none.
205438032Speter*/
205538032Speter
205638032Spetervoid
205738032Spetersetuserenv(envar, value)
205838032Speter	const char *envar;
205938032Speter	const char *value;
206038032Speter{
206138032Speter	int i;
206238032Speter	char **evp = UserEnviron;
206338032Speter	char *p;
206438032Speter
206538032Speter	if (value == NULL)
206638032Speter	{
206738032Speter		value = getextenv(envar);
206838032Speter		if (value == NULL)
206938032Speter			return;
207038032Speter	}
207138032Speter
207238032Speter	i = strlen(envar);
207338032Speter	p = (char *) xalloc(strlen(value) + i + 2);
207438032Speter	strcpy(p, envar);
207538032Speter	p[i++] = '=';
207638032Speter	strcpy(&p[i], value);
207738032Speter
207838032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
207938032Speter		evp++;
208038032Speter	if (*evp != NULL)
208138032Speter	{
208238032Speter		*evp++ = p;
208338032Speter	}
208438032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
208538032Speter	{
208638032Speter		*evp++ = p;
208738032Speter		*evp = NULL;
208838032Speter	}
208938032Speter
209038032Speter	/* make sure it is in our environment as well */
209138032Speter	if (putenv(p) < 0)
209238032Speter		syserr("setuserenv: putenv(%s) failed", p);
209338032Speter}
209438032Speter/*
209538032Speter**  DUMPSTATE -- dump state
209638032Speter**
209738032Speter**	For debugging.
209838032Speter*/
209938032Speter
210038032Spetervoid
210138032Speterdumpstate(when)
210238032Speter	char *when;
210338032Speter{
210438032Speter	register char *j = macvalue('j', CurEnv);
210538032Speter	int rs;
210638032Speter
210738032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
210838032Speter		"--- dumping state on %s: $j = %s ---",
210938032Speter		when,
211038032Speter		j == NULL ? "<NULL>" : j);
211138032Speter	if (j != NULL)
211238032Speter	{
211338032Speter		if (!wordinclass(j, 'w'))
211438032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
211538032Speter				"*** $j not in $=w ***");
211638032Speter	}
211738032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
211838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
211938032Speter	printopenfds(TRUE);
212038032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
212138032Speter	mci_dump_all(TRUE);
212238032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
212338032Speter	if (rs > 0)
212438032Speter	{
212538032Speter		int stat;
212638032Speter		register char **pvp;
212738032Speter		char *pv[MAXATOM + 1];
212838032Speter
212938032Speter		pv[0] = NULL;
213038032Speter		stat = rewrite(pv, rs, 0, CurEnv);
213138032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
213238032Speter		       "--- ruleset debug_dumpstate returns stat %d, pv: ---",
213338032Speter		       stat);
213438032Speter		for (pvp = pv; *pvp != NULL; pvp++)
213538032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
213638032Speter	}
213738032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
213838032Speter}
213938032Speter
214038032Speter
214138032Speter/* ARGSUSED */
214238032SpeterSIGFUNC_DECL
214338032Spetersigusr1(sig)
214438032Speter	int sig;
214538032Speter{
214638032Speter	dumpstate("user signal");
214738032Speter	return SIGFUNC_RETURN;
214838032Speter}
214938032Speter
215038032Speter
215138032Speter/* ARGSUSED */
215238032SpeterSIGFUNC_DECL
215338032Spetersighup(sig)
215438032Speter	int sig;
215538032Speter{
215638032Speter	if (SaveArgv[0][0] != '/')
215738032Speter	{
215838032Speter		if (LogLevel > 3)
215938032Speter			sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
216038032Speter		exit(EX_OSFILE);
216138032Speter	}
216238032Speter	if (LogLevel > 3)
216338032Speter		sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
216438032Speter	alarm(0);
216538032Speter	releasesignal(SIGHUP);
216638032Speter	if (drop_privileges(TRUE) != EX_OK)
216738032Speter	{
216838032Speter		if (LogLevel > 0)
216938032Speter			sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
217038032Speter				RunAsUid, RunAsGid);
217138032Speter		exit(EX_OSERR);
217238032Speter	}
217338032Speter	execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
217438032Speter	if (LogLevel > 0)
217538032Speter		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
217638032Speter	exit(EX_OSFILE);
217738032Speter}
217838032Speter/*
217938032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
218038032Speter**
218138032Speter**	Parameters:
218238032Speter**		to_real_uid -- if set, drop to the real uid instead
218338032Speter**			of the RunAsUser.
218438032Speter**
218538032Speter**	Returns:
218638032Speter**		EX_OSERR if the setuid failed.
218738032Speter**		EX_OK otherwise.
218838032Speter*/
218938032Speter
219038032Speterint
219138032Speterdrop_privileges(to_real_uid)
219238032Speter	bool to_real_uid;
219338032Speter{
219438032Speter	int rval = EX_OK;
219538032Speter	GIDSET_T emptygidset[1];
219638032Speter
219738032Speter	if (tTd(47, 1))
219838032Speter		printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
219938032Speter			(int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
220038032Speter
220138032Speter	if (to_real_uid)
220238032Speter	{
220338032Speter		RunAsUserName = RealUserName;
220438032Speter		RunAsUid = RealUid;
220538032Speter		RunAsGid = RealGid;
220638032Speter	}
220738032Speter
220838032Speter	/* make sure no one can grab open descriptors for secret files */
220938032Speter	endpwent();
221038032Speter
221138032Speter	/* reset group permissions; these can be set later */
221238032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
221338032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
221438032Speter		rval = EX_OSERR;
221538032Speter
221638032Speter	/* reset primary group and user id */
221738032Speter	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
221838032Speter		rval = EX_OSERR;
221938032Speter	if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
222038032Speter		rval = EX_OSERR;
222138032Speter	if (tTd(47, 5))
222238032Speter	{
222338032Speter		printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
222438032Speter			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
222538032Speter		printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
222638032Speter	}
222738032Speter	return rval;
222838032Speter}
222938032Speter/*
223038032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
223138032Speter**
223238032Speter**	Used to make sure that stdin/out/err are allocated on startup
223338032Speter**
223438032Speter**	Parameters:
223538032Speter**		fd -- the file descriptor to be filled.
223638032Speter**		where -- a string used for logging.  If NULL, this is
223738032Speter**			being called on startup, and logging should
223838032Speter**			not be done.
223938032Speter**
224038032Speter**	Returns:
224138032Speter**		none
224238032Speter*/
224338032Speter
224438032Spetervoid
224538032Speterfill_fd(fd, where)
224638032Speter	int fd;
224738032Speter	char *where;
224838032Speter{
224938032Speter	int i;
225038032Speter	struct stat stbuf;
225138032Speter
225238032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
225338032Speter		return;
225438032Speter
225538032Speter	if (where != NULL)
225638032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
225738032Speter	else
225838032Speter		MissingFds |= 1 << fd;
225938032Speter	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
226038032Speter	if (i < 0)
226138032Speter	{
226238032Speter		syserr("!fill_fd: %s: cannot open /dev/null",
226338032Speter			where == NULL ? "startup" : where);
226438032Speter	}
226538032Speter	if (fd != i)
226638032Speter	{
226738032Speter		(void) dup2(i, fd);
226838032Speter		(void) close(i);
226938032Speter	}
227038032Speter}
227138032Speter/*
227238032Speter**  TESTMODELINE -- process a test mode input line
227338032Speter**
227438032Speter**	Parameters:
227538032Speter**		line -- the input line.
227638032Speter**		e -- the current environment.
227738032Speter**	Syntax:
227838032Speter**		#  a comment
227938032Speter**		.X process X as a configuration line
228038032Speter**		=X dump a configuration item (such as mailers)
228138032Speter**		$X dump a macro or class
228238032Speter**		/X try an activity
228338032Speter**		X  normal process through rule set X
228438032Speter*/
228538032Speter
228638032Spetervoid
228738032Spetertestmodeline(line, e)
228838032Speter	char *line;
228938032Speter	ENVELOPE *e;
229038032Speter{
229138032Speter	register char *p;
229238032Speter	char *q;
229338032Speter	auto char *delimptr;
229438032Speter	int mid;
229538032Speter	int i, rs;
229638032Speter	STAB *map;
229738032Speter	char **s;
229838032Speter	struct rewrite *rw;
229938032Speter	ADDRESS a;
230038032Speter	static int tryflags = RF_COPYNONE;
230138032Speter	char exbuf[MAXLINE];
230238032Speter	extern bool invalidaddr __P((char *, char *));
230338032Speter	extern char *crackaddr __P((char *));
230438032Speter	extern void dump_class __P((STAB *, int));
230538032Speter	extern void translate_dollars __P((char *));
230638032Speter	extern void help __P((char *));
230738032Speter
230838032Speter	switch (line[0])
230938032Speter	{
231038032Speter	  case '#':
231138032Speter	  case 0:
231238032Speter		return;
231338032Speter
231438032Speter	  case '?':
231538032Speter		help("-bt");
231638032Speter		return;
231738032Speter
231838032Speter	  case '.':		/* config-style settings */
231938032Speter		switch (line[1])
232038032Speter		{
232138032Speter		  case 'D':
232238032Speter			mid = macid(&line[2], &delimptr);
232338032Speter			if (mid == '\0')
232438032Speter				return;
232538032Speter			translate_dollars(delimptr);
232638032Speter			define(mid, newstr(delimptr), e);
232738032Speter			break;
232838032Speter
232938032Speter		  case 'C':
233038032Speter			if (line[2] == '\0')	/* not to call syserr() */
233138032Speter				return;
233238032Speter
233338032Speter			mid = macid(&line[2], &delimptr);
233438032Speter			if (mid == '\0')
233538032Speter				return;
233638032Speter			translate_dollars(delimptr);
233738032Speter			expand(delimptr, exbuf, sizeof exbuf, e);
233838032Speter			p = exbuf;
233938032Speter			while (*p != '\0')
234038032Speter			{
234138032Speter				register char *wd;
234238032Speter				char delim;
234338032Speter
234438032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
234538032Speter					p++;
234638032Speter				wd = p;
234738032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
234838032Speter					p++;
234938032Speter				delim = *p;
235038032Speter				*p = '\0';
235138032Speter				if (wd[0] != '\0')
235238032Speter					setclass(mid, wd);
235338032Speter				*p = delim;
235438032Speter			}
235538032Speter			break;
235638032Speter
235738032Speter		  case '\0':
235838032Speter			printf("Usage: .[DC]macro value(s)\n");
235938032Speter			break;
236038032Speter
236138032Speter		  default:
236238032Speter			printf("Unknown \".\" command %s\n", line);
236338032Speter			break;
236438032Speter		}
236538032Speter		return;
236638032Speter
236738032Speter	  case '=':		/* config-style settings */
236838032Speter		switch (line[1])
236938032Speter		{
237038032Speter		  case 'S':		/* dump rule set */
237138032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
237238032Speter			if (rs < 0)
237338032Speter			{
237438032Speter				printf("Undefined ruleset %s\n", &line[2]);
237538032Speter				return;
237638032Speter			}
237738032Speter			rw = RewriteRules[rs];
237838032Speter			if (rw == NULL)
237938032Speter				return;
238038032Speter			do
238138032Speter			{
238238032Speter				putchar('R');
238338032Speter				s = rw->r_lhs;
238438032Speter				while (*s != NULL)
238538032Speter				{
238638032Speter					xputs(*s++);
238738032Speter					putchar(' ');
238838032Speter				}
238938032Speter				putchar('\t');
239038032Speter				putchar('\t');
239138032Speter				s = rw->r_rhs;
239238032Speter				while (*s != NULL)
239338032Speter				{
239438032Speter					xputs(*s++);
239538032Speter					putchar(' ');
239638032Speter				}
239738032Speter				putchar('\n');
239838032Speter			} while ((rw = rw->r_next) != NULL);
239938032Speter			break;
240038032Speter
240138032Speter		  case 'M':
240238032Speter			for (i = 0; i < MAXMAILERS; i++)
240338032Speter			{
240438032Speter				if (Mailer[i] != NULL)
240538032Speter					printmailer(Mailer[i]);
240638032Speter			}
240738032Speter			break;
240838032Speter
240938032Speter		  case '\0':
241038032Speter			printf("Usage: =Sruleset or =M\n");
241138032Speter			break;
241238032Speter
241338032Speter		  default:
241438032Speter			printf("Unknown \"=\" command %s\n", line);
241538032Speter			break;
241638032Speter		}
241738032Speter		return;
241838032Speter
241938032Speter	  case '-':		/* set command-line-like opts */
242038032Speter		switch (line[1])
242138032Speter		{
242238032Speter		  case 'd':
242338032Speter			tTflag(&line[2]);
242438032Speter			break;
242538032Speter
242638032Speter		  case '\0':
242738032Speter			printf("Usage: -d{debug arguments}\n");
242838032Speter			break;
242938032Speter
243038032Speter		  default:
243138032Speter			printf("Unknown \"-\" command %s\n", line);
243238032Speter			break;
243338032Speter		}
243438032Speter		return;
243538032Speter
243638032Speter	  case '$':
243738032Speter		if (line[1] == '=')
243838032Speter		{
243938032Speter			mid = macid(&line[2], NULL);
244038032Speter			if (mid != '\0')
244138032Speter				stabapply(dump_class, mid);
244238032Speter			return;
244338032Speter		}
244438032Speter		mid = macid(&line[1], NULL);
244538032Speter		if (mid == '\0')
244638032Speter			return;
244738032Speter		p = macvalue(mid, e);
244838032Speter		if (p == NULL)
244938032Speter			printf("Undefined\n");
245038032Speter		else
245138032Speter		{
245238032Speter			xputs(p);
245338032Speter			printf("\n");
245438032Speter		}
245538032Speter		return;
245638032Speter
245738032Speter	  case '/':		/* miscellaneous commands */
245838032Speter		p = &line[strlen(line)];
245938032Speter		while (--p >= line && isascii(*p) && isspace(*p))
246038032Speter			*p = '\0';
246138032Speter		p = strpbrk(line, " \t");
246238032Speter		if (p != NULL)
246338032Speter		{
246438032Speter			while (isascii(*p) && isspace(*p))
246538032Speter				*p++ = '\0';
246638032Speter		}
246738032Speter		else
246838032Speter			p = "";
246938032Speter		if (line[1] == '\0')
247038032Speter		{
247138032Speter			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
247238032Speter			return;
247338032Speter		}
247438032Speter		if (strcasecmp(&line[1], "mx") == 0)
247538032Speter		{
247638032Speter#if NAMED_BIND
247738032Speter			/* look up MX records */
247838032Speter			int nmx;
247938032Speter			auto int rcode;
248038032Speter			char *mxhosts[MAXMXHOSTS + 1];
248138032Speter
248238032Speter			if (*p == '\0')
248338032Speter			{
248438032Speter				printf("Usage: /mx address\n");
248538032Speter				return;
248638032Speter			}
248738032Speter			nmx = getmxrr(p, mxhosts, FALSE, &rcode);
248838032Speter			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
248938032Speter			for (i = 0; i < nmx; i++)
249038032Speter				printf("\t%s\n", mxhosts[i]);
249138032Speter#else
249238032Speter			printf("No MX code compiled in\n");
249338032Speter#endif
249438032Speter		}
249538032Speter		else if (strcasecmp(&line[1], "canon") == 0)
249638032Speter		{
249738032Speter			char host[MAXHOSTNAMELEN];
249838032Speter
249938032Speter			if (*p == '\0')
250038032Speter			{
250138032Speter				printf("Usage: /canon address\n");
250238032Speter				return;
250338032Speter			}
250438032Speter			else if (strlen(p) >= sizeof host)
250538032Speter			{
250638032Speter				printf("Name too long\n");
250738032Speter				return;
250838032Speter			}
250938032Speter			strcpy(host, p);
251038032Speter			(void) getcanonname(host, sizeof(host), HasWildcardMX);
251138032Speter			printf("getcanonname(%s) returns %s\n", p, host);
251238032Speter		}
251338032Speter		else if (strcasecmp(&line[1], "map") == 0)
251438032Speter		{
251538032Speter			auto int rcode = EX_OK;
251638032Speter			char *av[2];
251738032Speter
251838032Speter			if (*p == '\0')
251938032Speter			{
252038032Speter				printf("Usage: /map mapname key\n");
252138032Speter				return;
252238032Speter			}
252338032Speter			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
252438032Speter				continue;
252538032Speter			if (*q == '\0')
252638032Speter			{
252738032Speter				printf("No key specified\n");
252838032Speter				return;
252938032Speter			}
253038032Speter			*q++ = '\0';
253138032Speter			map = stab(p, ST_MAP, ST_FIND);
253238032Speter			if (map == NULL)
253338032Speter			{
253438032Speter				printf("Map named \"%s\" not found\n", p);
253538032Speter				return;
253638032Speter			}
253738032Speter			if (!bitset(MF_OPEN, map->s_map.map_mflags))
253838032Speter			{
253938032Speter				printf("Map named \"%s\" not open\n", p);
254038032Speter				return;
254138032Speter			}
254238032Speter			printf("map_lookup: %s (%s) ", p, q);
254338032Speter			av[0] = q;
254438032Speter			av[1] = NULL;
254538032Speter			p = (*map->s_map.map_class->map_lookup)
254638032Speter					(&map->s_map, q, av, &rcode);
254738032Speter			if (p == NULL)
254838032Speter				printf("no match (%d)\n", rcode);
254938032Speter			else
255038032Speter				printf("returns %s (%d)\n", p, rcode);
255138032Speter		}
255238032Speter		else if (strcasecmp(&line[1], "try") == 0)
255338032Speter		{
255438032Speter			MAILER *m;
255538032Speter			STAB *s;
255638032Speter			auto int rcode = EX_OK;
255738032Speter
255838032Speter			q = strpbrk(p, " \t");
255938032Speter			if (q != NULL)
256038032Speter			{
256138032Speter				while (isascii(*q) && isspace(*q))
256238032Speter					*q++ = '\0';
256338032Speter			}
256438032Speter			if (q == NULL || *q == '\0')
256538032Speter			{
256638032Speter				printf("Usage: /try mailer address\n");
256738032Speter				return;
256838032Speter			}
256938032Speter			s = stab(p, ST_MAILER, ST_FIND);
257038032Speter			if (s == NULL)
257138032Speter			{
257238032Speter				printf("Unknown mailer %s\n", p);
257338032Speter				return;
257438032Speter			}
257538032Speter			m = s->s_mailer;
257638032Speter			printf("Trying %s %s address %s for mailer %s\n",
257738032Speter				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
257838032Speter				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
257938032Speter				q, p);
258038032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
258138032Speter			printf("Rcode = %d, addr = %s\n",
258238032Speter				rcode, p == NULL ? "<NULL>" : p);
258338032Speter			e->e_to = NULL;
258438032Speter		}
258538032Speter		else if (strcasecmp(&line[1], "tryflags") == 0)
258638032Speter		{
258738032Speter			if (*p == '\0')
258838032Speter			{
258938032Speter				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
259038032Speter				return;
259138032Speter			}
259238032Speter			for (; *p != '\0'; p++)
259338032Speter			{
259438032Speter				switch (*p)
259538032Speter				{
259638032Speter				  case 'H':
259738032Speter				  case 'h':
259838032Speter					tryflags |= RF_HEADERADDR;
259938032Speter					break;
260038032Speter
260138032Speter				  case 'E':
260238032Speter				  case 'e':
260338032Speter					tryflags &= ~RF_HEADERADDR;
260438032Speter					break;
260538032Speter
260638032Speter				  case 'S':
260738032Speter				  case 's':
260838032Speter					tryflags |= RF_SENDERADDR;
260938032Speter					break;
261038032Speter
261138032Speter				  case 'R':
261238032Speter				  case 'r':
261338032Speter					tryflags &= ~RF_SENDERADDR;
261438032Speter					break;
261538032Speter				}
261638032Speter			}
261738032Speter		}
261838032Speter		else if (strcasecmp(&line[1], "parse") == 0)
261938032Speter		{
262038032Speter			if (*p == '\0')
262138032Speter			{
262238032Speter				printf("Usage: /parse address\n");
262338032Speter				return;
262438032Speter			}
262538032Speter			q = crackaddr(p);
262638032Speter			printf("Cracked address = ");
262738032Speter			xputs(q);
262838032Speter			printf("\nParsing %s %s address\n",
262938032Speter				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
263038032Speter				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
263138032Speter			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
263238032Speter				printf("Cannot parse\n");
263338032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
263438032Speter				printf("mailer %s, host %s, user %s\n",
263538032Speter					a.q_mailer->m_name, a.q_host, a.q_user);
263638032Speter			else
263738032Speter				printf("mailer %s, user %s\n",
263838032Speter					a.q_mailer->m_name, a.q_user);
263938032Speter			e->e_to = NULL;
264038032Speter		}
264138032Speter		else
264238032Speter		{
264338032Speter			printf("Unknown \"/\" command %s\n", line);
264438032Speter		}
264538032Speter		return;
264638032Speter	}
264738032Speter
264838032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
264938032Speter		continue;
265038032Speter	q = p;
265138032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
265238032Speter		p++;
265338032Speter	if (*p == '\0')
265438032Speter	{
265538032Speter		printf("No address!\n");
265638032Speter		return;
265738032Speter	}
265838032Speter	*p = '\0';
265938032Speter	if (invalidaddr(p + 1, NULL))
266038032Speter		return;
266138032Speter	do
266238032Speter	{
266338032Speter		register char **pvp;
266438032Speter		char pvpbuf[PSBUFSIZE];
266538032Speter
266638032Speter		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
266738032Speter			      &delimptr, NULL);
266838032Speter		if (pvp == NULL)
266938032Speter			continue;
267038032Speter		p = q;
267138032Speter		while (*p != '\0')
267238032Speter		{
267338032Speter			int stat;
267438032Speter
267538032Speter			rs = strtorwset(p, NULL, ST_FIND);
267638032Speter			if (rs < 0)
267738032Speter			{
267838032Speter				printf("Undefined ruleset %s\n", p);
267938032Speter				break;
268038032Speter			}
268138032Speter			stat = rewrite(pvp, rs, 0, e);
268238032Speter			if (stat != EX_OK)
268338032Speter				printf("== Ruleset %s (%d) status %d\n",
268438032Speter					p, rs, stat);
268538032Speter			while (*p != '\0' && *p++ != ',')
268638032Speter				continue;
268738032Speter		}
268838032Speter	} while (*(p = delimptr) != '\0');
268938032Speter}
269038032Speter
269138032Speter
269238032Spetervoid
269338032Speterdump_class(s, id)
269438032Speter	register STAB *s;
269538032Speter	int id;
269638032Speter{
269738032Speter	if (s->s_type != ST_CLASS)
269838032Speter		return;
269938032Speter	if (bitnset(id & 0xff, s->s_class))
270038032Speter		printf("%s\n", s->s_name);
270138032Speter}
2702