Sendmail source for the 3B1/7300 (part 7/8)

David H. Brierley dave at galaxia.Newport.RI.US
Sat Feb 25 12:29:43 AEST 1989


----- cut here and feed to /bin/sh -----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 7 (of 8)."
# Contents:  src/main.c src/parseaddr.c
# Wrapped by dave at galaxia on Fri Feb 24 20:24:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/main.c'\"
else
echo shar: Extracting \"'src/main.c'\" \(21371 characters\)
sed "s/^X//" >'src/main.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)main.c	5.13 (Berkeley) 3/24/87";
X#endif not lint
X
X# define  _DEFINE
X# include <signal.h>
X# include <sgtty.h>
X# include "sendmail.h"
X
X# ifdef lint
Xchar	edata, end;
X# endif lint
X
X/*
X**  SENDMAIL -- Post mail to a set of destinations.
X**
X**	This is the basic mail router.  All user mail programs should
X**	call this routine to actually deliver mail.  Sendmail in
X**	turn calls a bunch of mail servers that do the real work of
X**	delivering the mail.
X**
X**	Sendmail is driven by tables read in from /usr/lib/sendmail.cf
X**	(read by readcf.c).  Some more static configuration info,
X**	including some code that you may want to tailor for your
X**	installation, is in conf.c.  You may also want to touch
X**	daemon.c (if you have some other IPC mechanism), acct.c
X**	(to change your accounting), names.c (to adjust the name
X**	server mechanism).
X**
X**	Usage:
X**		/usr/lib/sendmail [flags] addr ...
X**
X**		See the associated documentation for details.
X**
X**	Author:
X**		Eric Allman, UCB/INGRES (until 10/81)
X**			     Britton-Lee, Inc., purveyors of fine
X**				database computers (from 11/81)
X**		The support of the INGRES Project and Britton-Lee is
X**			gratefully acknowledged.  Britton-Lee in
X**			particular had absolutely nothing to gain from
X**			my involvement in this project.
X*/
X
X
X
X
X
Xint		NextMailer;	/* "free" index into Mailer struct */
Xchar		*FullName;	/* sender's full name */
XENVELOPE	BlankEnvelope;	/* a "blank" envelope */
XENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
XADDRESS		NullAddress =	/* a null address */
X		{ "", "", "" };
X
X/*
X**  Pointers for setproctitle.
X**	This allows "ps" listings to give more useful information.
X**	These must be kept out of BSS for frozen configuration files
X**		to work.
X*/
X
X# ifdef SETPROCTITLE
Xchar		**Argv = NULL;		/* pointer to argument vector */
Xchar		*LastArgv = NULL;	/* end of argv */
X# endif SETPROCTITLE
X
X#ifdef DAEMON
X#ifndef SMTP
XERROR %%%%   Cannot have daemon mode without SMTP   %%%% ERROR
X#endif SMTP
X#endif DAEMON
X
X
X
X
X
X
Xmain(argc, argv, envp)
X	int argc;
X	char **argv;
X	char **envp;
X{
X	register char *p;
X	char **av;
X	extern int finis();
X	extern char Version[];
X	char *from;
X	typedef int (*fnptr)();
X	STAB *st;
X	register int i;
X	bool readconfig = TRUE;
X	bool queuemode = FALSE;		/* process queue requests */
X	bool nothaw;
X	static bool reenter = FALSE;
X	char jbuf[30];			/* holds MyHostName */
X	extern bool safefile();
X	extern time_t convtime();
X	extern putheader(), putbody();
X	extern ENVELOPE *newenvelope();
X	extern intsig();
X	extern char **myhostname();
X	extern char *arpadate();
X	extern char **environ;
X
X	/*
X	**  Check to see if we reentered.
X	**	This would normally happen if e_putheader or e_putbody
X	**	were NULL when invoked.
X	*/
X
X	if (reenter)
X	{
X		syserr("main: reentered!");
X		abort();
X	}
X	reenter = TRUE;
X
X#ifdef notdef
X	/*
X	 * the following line does not have the effect the author expected --
X	 * it makes the time always use eastern time EST, EDT -- allyn
X	 */
X
X	/* Enforce use of local time */
X	unsetenv("TZ");
X#endif
X
X	/*
X	**  Be sure we have enough file descriptors.
X	**	But also be sure that 0, 1, & 2 are open.
X	*/
X
X	i = open("/dev/null", 2);
X	while (i >= 0 && i < 2)
X		i = dup(i);
X	for (i = 3; i < 50; i++)
X		(void) close(i);
X	errno = 0;
X
X	/*
X	**  Set default values for variables.
X	**	These cannot be in initialized data space.
X	*/
X
X	setdefaults();
X
X	/* set up the blank envelope */
X	BlankEnvelope.e_puthdr = putheader;
X	BlankEnvelope.e_putbody = putbody;
X	BlankEnvelope.e_xfp = NULL;
X	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
X	CurEnv = &BlankEnvelope;
X	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
X
X	/*
X	**  Do a quick prescan of the argument list.
X	**	We do this to find out if we can potentially thaw the
X	**	configuration file.  If not, we do the thaw now so that
X	**	the argument processing applies to this run rather than
X	**	to the run that froze the configuration.
X	*/
X
X	argv[argc] = NULL;
X	av = argv;
X	nothaw = FALSE;
X	while ((p = *++av) != NULL)
X	{
X		if (strncmp(p, "-C", 2) == 0)
X		{
X			ConfFile = &p[2];
X			if (ConfFile[0] == '\0')
X				ConfFile = "sendmail.cf";
X			(void) setgid(getrgid());
X			(void) setuid(getruid());
X			nothaw = TRUE;
X		}
X		else if (strncmp(p, "-bz", 3) == 0)
X			nothaw = TRUE;
X# ifdef DEBUG
X		else if (strncmp(p, "-d", 2) == 0)
X		{
X			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X			tTflag(&p[2]);
X			setbuf(stdout, (char *) NULL);
X			printf("Version %s\n", Version);
X		}
X# endif DEBUG
X	}
X	if (!nothaw)
X		readconfig = !thaw(FreezeFile);
X
X	/* reset the environment after the thaw */
X	for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
X		UserEnviron[i] = newstr(envp[i]);
X	UserEnviron[i] = NULL;
X	environ = UserEnviron;
X
X# ifdef SETPROCTITLE
X	/*
X	**  Save start and extent of argv for setproctitle.
X	*/
X
X	Argv = argv;
X	if (i > 0)
X		LastArgv = envp[i - 1] + strlen(envp[i - 1]);
X	else
X		LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
X# endif SETPROCTITLE
X
X	/*
X	**  Now do basic initialization
X	*/
X
X	InChannel = stdin;
X	OutChannel = stdout;
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, intsig);
X	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGHUP, intsig);
X	(void) signal(SIGTERM, intsig);
X	(void) signal(SIGPIPE, SIG_IGN);
X	OldUmask = umask(0);
X	OpMode = MD_DELIVER;
X	MotherPid = getpid();
X# ifndef V6
X	FullName = getenv("NAME");
X# endif V6
X
X# ifdef LOG
X	openlog("sendmail", LOG_PID, LOG_MAIL);
X# endif LOG
X	errno = 0;
X	from = NULL;
X
X	if (readconfig)
X	{
X		/* initialize some macros, etc. */
X		initmacros();
X
X		/* hostname */
X		av = myhostname(jbuf, sizeof jbuf);
X		if (jbuf[0] != '\0')
X		{
X#ifdef DEBUG
X			if (tTd(0, 4))
X				printf("canonical name: %s\n", jbuf);
X#endif DEBUG
X			p = newstr(jbuf);
X			define('w', p, CurEnv);
X			setclass('w', p);
X		}
X		while (av != NULL && *av != NULL)
X		{
X#ifdef DEBUG
X			if (tTd(0, 4))
X				printf("\ta.k.a.: %s\n", *av);
X#endif DEBUG
X			setclass('w', *av++);
X		}
X
X		/* version */
X		define('v', Version, CurEnv);
X	}
X
X	/* current time */
X	define('b', arpadate((char *) NULL), CurEnv);
X
X	/*
X	** Crack argv.
X	*/
X
X	av = argv;
X	p = rindex(*av, '/');
X	if (p++ == NULL)
X		p = *av;
X	if (strcmp(p, "newaliases") == 0)
X		OpMode = MD_INITALIAS;
X	else if (strcmp(p, "mailq") == 0)
X		OpMode = MD_PRINT;
X	else if (strcmp(p, "smtpd") == 0)
X		OpMode = MD_DAEMON;
X	while ((p = *++av) != NULL && p[0] == '-')
X	{
X		switch (p[1])
X		{
X		  case 'b':	/* operations mode */
X			switch (p[2])
X			{
X			  case MD_DAEMON:
X# ifndef DAEMON
X				syserr("Daemon mode not implemented");
X				break;
X# endif DAEMON
X			  case MD_SMTP:
X# ifndef SMTP
X				syserr("I don't speak SMTP");
X				break;
X# endif SMTP
X			  case MD_ARPAFTP:
X			  case MD_DELIVER:
X			  case MD_VERIFY:
X			  case MD_TEST:
X			  case MD_INITALIAS:
X			  case MD_PRINT:
X			  case MD_FREEZE:
X				OpMode = p[2];
X				break;
X
X			  default:
X				syserr("Invalid operation mode %c", p[2]);
X				break;
X			}
X			break;
X
X		  case 'C':	/* select configuration file (already done) */
X			break;
X
X# ifdef DEBUG
X		  case 'd':	/* debugging -- redo in case frozen */
X			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X			tTflag(&p[2]);
X			setbuf(stdout, (char *) NULL);
X			break;
X# endif DEBUG
X
X		  case 'f':	/* from address */
X		  case 'r':	/* obsolete -f flag */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X			{
X				p = *++av;
X				if (p == NULL || *p == '-')
X				{
X					syserr("No \"from\" person");
X					av--;
X					break;
X				}
X			}
X			if (from != NULL)
X			{
X				syserr("More than one \"from\" person");
X				break;
X			}
X			from = newstr(p);
X			break;
X
X		  case 'F':	/* set full name */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X			{
X				syserr("Bad -F flag");
X				av--;
X				break;
X			}
X			FullName = newstr(p);
X			break;
X
X		  case 'h':	/* hop count */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
X			{
X				syserr("Bad hop count (%s)", p);
X				av--;
X				break;
X			}
X			CurEnv->e_hopcount = atoi(p);
X			break;
X		
X		  case 'n':	/* don't alias */
X			NoAlias = TRUE;
X			break;
X
X		  case 'o':	/* set option */
X			setoption(p[2], &p[3], FALSE, TRUE);
X			break;
X
X		  case 'q':	/* run queue files at intervals */
X# ifdef QUEUE
X			queuemode = TRUE;
X			QueueIntvl = convtime(&p[2]);
X# else QUEUE
X			syserr("I don't know about queues");
X# endif QUEUE
X			break;
X
X		  case 't':	/* read recipients from message */
X			GrabTo = TRUE;
X			break;
X
X			/* compatibility flags */
X		  case 'c':	/* connect to non-local mailers */
X		  case 'e':	/* error message disposition */
X		  case 'i':	/* don't let dot stop me */
X		  case 'm':	/* send to me too */
X		  case 'T':	/* set timeout interval */
X		  case 'v':	/* give blow-by-blow description */
X			setoption(p[1], &p[2], FALSE, TRUE);
X			break;
X
X		  case 's':	/* save From lines in headers */
X			setoption('f', &p[2], FALSE, TRUE);
X			break;
X
X# ifdef DBM
X		  case 'I':	/* initialize alias DBM file */
X			OpMode = MD_INITALIAS;
X			break;
X# endif DBM
X		}
X	}
X
X	/*
X	**  Do basic initialization.
X	**	Read system control file.
X	**	Extract special fields for local use.
X	*/
X
X	if (OpMode == MD_FREEZE || readconfig)
X		readcf(ConfFile);
X
X	switch (OpMode)
X	{
X	  case MD_FREEZE:
X		/* this is critical to avoid forgeries of the frozen config */
X		(void) setgid(getgid());
X		(void) setuid(getuid());
X
X		/* freeze the configuration */
X		freeze(FreezeFile);
X		exit(EX_OK);
X
X	  case MD_INITALIAS:
X		Verbose = TRUE;
X		break;
X	}
X
X	/* do heuristic mode adjustment */
X	if (Verbose)
X	{
X		/* turn off noconnect option */
X		setoption('c', "F", TRUE, FALSE);
X
X		/* turn on interactive delivery */
X		setoption('d', "", TRUE, FALSE);
X	}
X
X	/* our name for SMTP codes */
X	expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
X	MyHostName = jbuf;
X
X	/* the indices of local and program mailers */
X	st = stab("local", ST_MAILER, ST_FIND);
X	if (st == NULL)
X		syserr("No local mailer defined");
X	else
X		LocalMailer = st->s_mailer;
X	st = stab("prog", ST_MAILER, ST_FIND);
X	if (st == NULL)
X		syserr("No prog mailer defined");
X	else
X		ProgMailer = st->s_mailer;
X
X	/* operate in queue directory */
X	if (chdir(QueueDir) < 0)
X	{
X		syserr("cannot chdir(%s)", QueueDir);
X		exit(EX_SOFTWARE);
X	}
X
X	/*
X	**  Do operation-mode-dependent initialization.
X	*/
X
X	switch (OpMode)
X	{
X	  case MD_PRINT:
X		/* print the queue */
X#ifdef QUEUE
X		dropenvelope(CurEnv);
X		printqueue();
X		exit(EX_OK);
X#else QUEUE
X		usrerr("No queue to print");
X		finis();
X#endif QUEUE
X
X	  case MD_INITALIAS:
X		/* initialize alias database */
X		initaliases(AliasFile, TRUE);
X		exit(EX_OK);
X
X	  case MD_DAEMON:
X		/* don't open alias database -- done in srvrsmtp */
X		break;
X
X	  default:
X		/* open the alias database */
X		initaliases(AliasFile, FALSE);
X		break;
X	}
X
X# ifdef DEBUG
X	if (tTd(0, 15))
X	{
X		/* print configuration table (or at least part of it) */
X		printrules();
X		for (i = 0; i < MAXMAILERS; i++)
X		{
X			register struct mailer *m = Mailer[i];
X			int j;
X
X			if (m == NULL)
X				continue;
X			printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
X				m->m_mailer, m->m_s_rwset, m->m_r_rwset,
X				m->m_maxsize);
X			for (j = '\0'; j <= '\177'; j++)
X				if (bitnset(j, m->m_flags))
X					(void) putchar(j);
X			printf(" E=");
X			xputs(m->m_eol);
X			printf("\n");
X		}
X	}
X# endif DEBUG
X
X	/*
X	**  Switch to the main envelope.
X	*/
X
X	CurEnv = newenvelope(&MainEnvelope);
X	MainEnvelope.e_flags = BlankEnvelope.e_flags;
X
X	/*
X	**  If test mode, read addresses from stdin and process.
X	*/
X
X	if (OpMode == MD_TEST)
X	{
X		char buf[MAXLINE];
X
X		printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
X		for (;;)
X		{
X			register char **pvp;
X			char *q;
X			extern char *DelimChar;
X
X			printf("> ");
X			(void) fflush(stdout);
X			if (fgets(buf, sizeof buf, stdin) == NULL)
X				finis();
X			for (p = buf; isspace(*p); *p++)
X				continue;
X			q = p;
X			while (*p != '\0' && !isspace(*p))
X				p++;
X			if (*p == '\0')
X				continue;
X			*p = '\0';
X			do
X			{
X				extern char **prescan();
X				char pvpbuf[PSBUFSIZE];
X
X				pvp = prescan(++p, ',', pvpbuf);
X				if (pvp == NULL)
X					continue;
X				rewrite(pvp, 3);
X				p = q;
X				while (*p != '\0')
X				{
X					rewrite(pvp, atoi(p));
X					while (*p != '\0' && *p++ != ',')
X						continue;
X				}
X			} while (*(p = DelimChar) != '\0');
X		}
X	}
X
X# ifdef QUEUE
X	/*
X	**  If collecting stuff from the queue, go start doing that.
X	*/
X
X	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
X	{
X		runqueue(FALSE);
X		finis();
X	}
X# endif QUEUE
X
X	/*
X	**  If a daemon, wait for a request.
X	**	getrequests will always return in a child.
X	**	If we should also be processing the queue, start
X	**		doing it in background.
X	**	We check for any errors that might have happened
X	**		during startup.
X	*/
X
X	if (OpMode == MD_DAEMON || QueueIntvl != 0)
X	{
X		if (!tTd(0, 1))
X		{
X			/* put us in background */
X			i = fork();
X			if (i < 0)
X				syserr("daemon: cannot fork");
X			if (i != 0)
X				exit(0);
X
X			/* get our pid right */
X			MotherPid = getpid();
X
X			/* disconnect from our controlling tty */
X			disconnect(TRUE);
X		}
X
X# ifdef QUEUE
X		if (queuemode)
X		{
X			runqueue(TRUE);
X			if (OpMode != MD_DAEMON)
X				for (;;)
X					pause();
X		}
X# endif QUEUE
X		dropenvelope(CurEnv);
X
X#ifdef DAEMON
X		getrequests();
X
X		/* at this point we are in a child: reset state */
X		OpMode = MD_SMTP;
X		(void) newenvelope(CurEnv);
X		openxscript(CurEnv);
X#endif DAEMON
X	}
X	
X# ifdef SMTP
X	/*
X	**  If running SMTP protocol, start collecting and executing
X	**  commands.  This will never return.
X	*/
X
X	if (OpMode == MD_SMTP)
X		smtp();
X# endif SMTP
X
X	/*
X	**  Do basic system initialization and set the sender
X	*/
X
X	initsys();
X	setsender(from);
X
X	if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
X	{
X		usrerr("Recipient names must be specified");
X
X		/* collect body for UUCP return */
X		if (OpMode != MD_VERIFY)
X			collect(FALSE);
X		finis();
X	}
X	if (OpMode == MD_VERIFY)
X		SendMode = SM_VERIFY;
X
X	/*
X	**  Scan argv and deliver the message to everyone.
X	*/
X
X	sendtoargv(av);
X
X	/* if we have had errors sofar, arrange a meaningful exit stat */
X	if (Errors > 0 && ExitStat == EX_OK)
X		ExitStat = EX_USAGE;
X
X	/*
X	**  Read the input mail.
X	*/
X
X	CurEnv->e_to = NULL;
X	if (OpMode != MD_VERIFY || GrabTo)
X		collect(FALSE);
X	errno = 0;
X
X	/* collect statistics */
X	if (OpMode != MD_VERIFY)
X		markstats(CurEnv, (ADDRESS *) NULL);
X
X# ifdef DEBUG
X	if (tTd(1, 1))
X		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
X# endif DEBUG
X
X	/*
X	**  Actually send everything.
X	**	If verifying, just ack.
X	*/
X
X	CurEnv->e_from.q_flags |= QDONTSEND;
X	CurEnv->e_to = NULL;
X	sendall(CurEnv, SM_DEFAULT);
X
X	/*
X	** All done.
X	*/
X
X	finis();
X}
X/*
X**  FINIS -- Clean up and exit.
X**
X**	Parameters:
X**		none
X**
X**	Returns:
X**		never
X**
X**	Side Effects:
X**		exits sendmail
X*/
X
Xfinis()
X{
X# ifdef DEBUG
X	if (tTd(2, 1))
X		printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
X# endif DEBUG
X
X	/* clean up temp files */
X	CurEnv->e_to = NULL;
X	dropenvelope(CurEnv);
X
X	/* post statistics */
X	poststats(StatFile);
X
X	/* and exit */
X# ifdef LOG
X	if (LogLevel > 11)
X		syslog(LOG_DEBUG, "finis, pid=%d", getpid());
X# endif LOG
X	if (ExitStat == EX_TEMPFAIL)
X		ExitStat = EX_OK;
X	exit(ExitStat);
X}
X/*
X**  INTSIG -- clean up on interrupt
X**
X**	This just arranges to exit.  It pessimises in that it
X**	may resend a message.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Unlocks the current job.
X*/
X
Xintsig()
X{
X	FileName = NULL;
X	unlockqueue(CurEnv);
X	exit(EX_OK);
X}
X/*
X**  INITMACROS -- initialize the macro system
X**
X**	This just involves defining some macros that are actually
X**	used internally as metasymbols to be themselves.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		initializes several macros to be themselves.
X*/
X
Xstruct metamac
X{
X	char	metaname;
X	char	metaval;
X};
X
Xstruct metamac	MetaMacros[] =
X{
X	/* LHS pattern matching characters */
X	'*', MATCHZANY,	'+', MATCHANY,	'-', MATCHONE,	'=', MATCHCLASS,
X	'~', MATCHNCLASS,
X
X	/* these are RHS metasymbols */
X	'#', CANONNET,	'@', CANONHOST,	':', CANONUSER,	'>', CALLSUBR,
X
X	/* the conditional operations */
X	'?', CONDIF,	'|', CONDELSE,	'.', CONDFI,
X
X	/* and finally the hostname lookup characters */
X	'[', HOSTBEGIN,	']', HOSTEND,
X
X	'\0'
X};
X
Xinitmacros()
X{
X	register struct metamac *m;
X	char buf[5];
X	register int c;
X
X	for (m = MetaMacros; m->metaname != '\0'; m++)
X	{
X		buf[0] = m->metaval;
X		buf[1] = '\0';
X		define(m->metaname, newstr(buf), CurEnv);
X	}
X	buf[0] = MATCHREPL;
X	buf[2] = '\0';
X	for (c = '0'; c <= '9'; c++)
X	{
X		buf[1] = c;
X		define(c, newstr(buf), CurEnv);
X	}
X}
X/*
X**  FREEZE -- freeze BSS & allocated memory
X**
X**	This will be used to efficiently load the configuration file.
X**
X**	Parameters:
X**		freezefile -- the name of the file to freeze to.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Writes BSS and malloc'ed memory to freezefile
X*/
X
Xunion frz
X{
X	char		frzpad[BUFSIZ];	/* insure we are on a BUFSIZ boundary */
X	struct
X	{
X		time_t	frzstamp;	/* timestamp on this freeze */
X		char	*frzbrk;	/* the current break */
X		char	*frzedata;	/* address of edata */
X		char	*frzend;	/* address of end */
X		char	frzver[252];	/* sendmail version */
X	} frzinfo;
X};
X
Xfreeze(freezefile)
X	char *freezefile;
X{
X	int f;
X	union frz fhdr;
X	extern char edata, end;
X	extern char *sbrk();
X	extern char Version[];
X
X	if (freezefile == NULL)
X		return;
X
X	/* try to open the freeze file */
X	f = creat(freezefile, FileMode);
X	if (f < 0)
X	{
X		syserr("Cannot freeze");
X		errno = 0;
X		return;
X	}
X
X	/* build the freeze header */
X	fhdr.frzinfo.frzstamp = curtime();
X	fhdr.frzinfo.frzbrk = sbrk(0);
X	fhdr.frzinfo.frzedata = &edata;
X	fhdr.frzinfo.frzend = &end;
X	(void) strcpy(fhdr.frzinfo.frzver, Version);
X
X	/* write out the freeze header */
X	if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
X	    write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X					(int) (fhdr.frzinfo.frzbrk - &edata))
X	{
X		syserr("Cannot freeze");
X	}
X
X	/* fine, clean up */
X	(void) close(f);
X}
X/*
X**  THAW -- read in the frozen configuration file.
X**
X**	Parameters:
X**		freezefile -- the name of the file to thaw from.
X**
X**	Returns:
X**		TRUE if it successfully read the freeze file.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		reads freezefile in to BSS area.
X*/
X
Xthaw(freezefile)
X	char *freezefile;
X{
X	int f;
X	union frz fhdr;
X	extern char edata;
X	extern char Version[];
X	extern caddr_t brk();
X
X	if (freezefile == NULL)
X		return (FALSE);
X
X	/* open the freeze file */
X	f = open(freezefile, 0);
X	if (f < 0)
X	{
X		errno = 0;
X		return (FALSE);
X	}
X
X	/* read in the header */
X	if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr ||
X	    fhdr.frzinfo.frzedata != &edata ||
X	    fhdr.frzinfo.frzend != &end ||
X	    strcmp(fhdr.frzinfo.frzver, Version) != 0)
X	{
X		(void) close(f);
X		return (FALSE);
X	}
X
X	/* arrange to have enough space */
X	if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1)
X	{
X		syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
X		(void) close(f);
X		return (FALSE);
X	}
X
X	/* now read in the freeze file */
X	if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X					(int) (fhdr.frzinfo.frzbrk - &edata))
X	{
X		/* oops!  we have trashed memory..... */
X		(void) write(2, "Cannot read freeze file\n", 24);
X		_exit(EX_SOFTWARE);
X	}
X
X	(void) close(f);
X	return (TRUE);
X}
X/*
X**  DISCONNECT -- remove our connection with any foreground process
X**
X**	Parameters:
X**		fulldrop -- if set, we should also drop the controlling
X**			TTY if possible -- this should only be done when
X**			setting up the daemon since otherwise UUCP can
X**			leave us trying to open a dialin, and we will
X**			wait for the carrier.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Trys to insure that we are immune to vagaries of
X**		the controlling tty.
X*/
X
Xdisconnect(fulldrop)
X	bool fulldrop;
X{
X	int fd;
X
X#ifdef DEBUG
X	if (tTd(52, 1))
X		printf("disconnect: In %d Out %d\n", fileno(InChannel),
X						fileno(OutChannel));
X	if (tTd(52, 5))
X	{
X		printf("don't\n");
X		return;
X	}
X#endif DEBUG
X
X	/* be sure we don't get nasty signals */
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X
X	/* we can't communicate with our caller, so.... */
X	HoldErrs = TRUE;
X	ErrorMode = EM_MAIL;
X	Verbose = FALSE;
X
X	/* all input from /dev/null */
X	if (InChannel != stdin)
X	{
X		(void) fclose(InChannel);
X		InChannel = stdin;
X	}
X	(void) freopen("/dev/null", "r", stdin);
X
X	/* output to the transcript */
X	if (OutChannel != stdout)
X	{
X		(void) fclose(OutChannel);
X		OutChannel = stdout;
X	}
X	if (CurEnv->e_xfp == NULL)
X		CurEnv->e_xfp = fopen("/dev/null", "w");
X	(void) fflush(stdout);
X	(void) close(1);
X	(void) close(2);
X	while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
X		continue;
X
X#ifdef TIOCNOTTY
X	/* drop our controlling TTY completely if possible */
X	if (fulldrop)
X	{
X		fd = open("/dev/tty", 2);
X		if (fd >= 0)
X		{
X			(void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
X			(void) close(fd);
X		}
X		(void) setpgrp(0, 0);
X		errno = 0;
X	}
X#endif TIOCNOTTY
X
X# ifdef LOG
X	if (LogLevel > 11)
X		syslog(LOG_DEBUG, "in background, pid=%d", getpid());
X# endif LOG
X
X	errno = 0;
X}
END_OF_FILE
if test 21371 -ne `wc -c <'src/main.c'`; then
    echo shar: \"'src/main.c'\" unpacked with wrong size!
fi
# end of 'src/main.c'
fi
if test -f 'src/parseaddr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/parseaddr.c'\"
else
echo shar: Extracting \"'src/parseaddr.c'\" \(24032 characters\)
sed "s/^X//" >'src/parseaddr.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)parseaddr.c	5.6 (Berkeley) 4/2/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  PARSEADDR -- Parse an address
X**
X**	Parses an address and breaks it up into three parts: a
X**	net to transmit the message on, the host to transmit it
X**	to, and a user on that host.  These are loaded into an
X**	ADDRESS header with the values squirreled away if necessary.
X**	The "user" part may not be a real user; the process may
X**	just reoccur on that machine.  For example, on a machine
X**	with an arpanet connection, the address
X**		csvax.bill at berkeley
X**	will break up to a "user" of 'csvax.bill' and a host
X**	of 'berkeley' -- to be transmitted over the arpanet.
X**
X**	Parameters:
X**		addr -- the address to parse.
X**		a -- a pointer to the address descriptor buffer.
X**			If NULL, a header will be created.
X**		copyf -- determines what shall be copied:
X**			-1 -- don't copy anything.  The printname
X**				(q_paddr) is just addr, and the
X**				user & host are allocated internally
X**				to parse.
X**			0 -- copy out the parsed user & host, but
X**				don't copy the printname.
X**			+1 -- copy everything.
X**		delim -- the character to terminate the address, passed
X**			to prescan.
X**
X**	Returns:
X**		A pointer to the address descriptor header (`a' if
X**			`a' is non-NULL).
X**		NULL on error.
X**
X**	Side Effects:
X**		none
X*/
X
X/* following delimiters are inherent to the internal algorithms */
X# define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
X
XADDRESS *
Xparseaddr(addr, a, copyf, delim)
X	char *addr;
X	register ADDRESS *a;
X	int copyf;
X	char delim;
X{
X	register char **pvp;
X	register struct mailer *m;
X	char pvpbuf[PSBUFSIZE];
X	extern char **prescan();
X	extern ADDRESS *buildaddr();
X
X	/*
X	**  Initialize and prescan address.
X	*/
X
X	CurEnv->e_to = addr;
X# ifdef DEBUG
X	if (tTd(20, 1))
X		printf("\n--parseaddr(%s)\n", addr);
X# endif DEBUG
X
X	pvp = prescan(addr, delim, pvpbuf);
X	if (pvp == NULL)
X		return (NULL);
X
X	/*
X	**  Apply rewriting rules.
X	**	Ruleset 0 does basic parsing.  It must resolve.
X	*/
X
X	rewrite(pvp, 3);
X	rewrite(pvp, 0);
X
X	/*
X	**  See if we resolved to a real mailer.
X	*/
X
X	if (pvp[0][0] != CANONNET)
X	{
X		setstat(EX_USAGE);
X		usrerr("cannot resolve name");
X		return (NULL);
X	}
X
X	/*
X	**  Build canonical address from pvp.
X	*/
X
X	a = buildaddr(pvp, a);
X	if (a == NULL)
X		return (NULL);
X	m = a->q_mailer;
X
X	/*
X	**  Make local copies of the host & user and then
X	**  transport them out.
X	*/
X
X	if (copyf > 0)
X	{
X		extern char *DelimChar;
X		char savec = *DelimChar;
X
X		*DelimChar = '\0';
X		a->q_paddr = newstr(addr);
X		*DelimChar = savec;
X	}
X	else
X		a->q_paddr = addr;
X
X	if (a->q_user == NULL)
X		a->q_user = "";
X	if (a->q_host == NULL)
X		a->q_host = "";
X
X	if (copyf >= 0)
X	{
X		a->q_host = newstr(a->q_host);
X		if (a->q_user != a->q_paddr)
X			a->q_user = newstr(a->q_user);
X	}
X
X	/*
X	**  Convert host name to lower case if requested.
X	**	User name will be done later.
X	*/
X
X	if (!bitnset(M_HST_UPPER, m->m_flags))
X		makelower(a->q_host);
X
X	/*
X	**  Compute return value.
X	*/
X
X# ifdef DEBUG
X	if (tTd(20, 1))
X	{
X		printf("parseaddr-->");
X		printaddr(a, FALSE);
X	}
X# endif DEBUG
X
X	return (a);
X}
X/*
X**  LOWERADDR -- map UPPER->lower case on addresses as requested.
X**
X**	Parameters:
X**		a -- address to be mapped.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xloweraddr(a)
X	register ADDRESS *a;
X{
X	register MAILER *m = a->q_mailer;
X
X	if (!bitnset(M_USR_UPPER, m->m_flags))
X		makelower(a->q_user);
X}
X/*
X**  PRESCAN -- Prescan name and make it canonical
X**
X**	Scans a name and turns it into a set of tokens.  This process
X**	deletes blanks and comments (in parentheses).
X**
X**	This routine knows about quoted strings and angle brackets.
X**
X**	There are certain subtleties to this routine.  The one that
X**	comes to mind now is that backslashes on the ends of names
X**	are silently stripped off; this is intentional.  The problem
X**	is that some versions of sndmsg (like at LBL) set the kill
X**	character to something other than @ when reading addresses;
X**	so people type "csvax.eric\@berkeley" -- which screws up the
X**	berknet mailer.
X**
X**	Parameters:
X**		addr -- the name to chomp.
X**		delim -- the delimiter for the address, normally
X**			'\0' or ','; \0 is accepted in any case.
X**			If '\t' then we are reading the .cf file.
X**		pvpbuf -- place to put the saved text -- note that
X**			the pointers are static.
X**
X**	Returns:
X**		A pointer to a vector of tokens.
X**		NULL on error.
X**
X**	Side Effects:
X**		sets DelimChar to point to the character matching 'delim'.
X*/
X
X/* states and character types */
X# define OPR		0	/* operator */
X# define ATM		1	/* atom */
X# define QST		2	/* in quoted string */
X# define SPC		3	/* chewing up spaces */
X# define ONE		4	/* pick up one character */
X
X# define NSTATES	5	/* number of states */
X# define TYPE		017	/* mask to select state type */
X
X/* meta bits for table */
X# define M		020	/* meta character; don't pass through */
X# define B		040	/* cause a break */
X# define MB		M|B	/* meta-break */
X
Xstatic short StateTab[NSTATES][NSTATES] =
X{
X   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
X	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
X	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
X	/*QST*/		QST,	QST,	OPR,	QST,	QST,
X	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
X	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
X};
X
X# define NOCHAR		-1	/* signal nothing in lookahead token */
X
Xchar	*DelimChar;		/* set to point to the delimiter */
X
Xchar **
Xprescan(addr, delim, pvpbuf)
X	char *addr;
X	char delim;
X	char pvpbuf[];
X{
X	register char *p;
X	register char *q;
X	register int c;
X	char **avp;
X	bool bslashmode;
X	int cmntcnt;
X	int anglecnt;
X	char *tok;
X	int state;
X	int newstate;
X	static char *av[MAXATOM+1];
X	extern int errno;
X
X	/* make sure error messages don't have garbage on them */
X	errno = 0;
X
X	q = pvpbuf;
X	bslashmode = FALSE;
X	cmntcnt = 0;
X	anglecnt = 0;
X	avp = av;
X	state = OPR;
X	c = NOCHAR;
X	p = addr;
X# ifdef DEBUG
X	if (tTd(22, 45))
X	{
X		printf("prescan: ");
X		xputs(p);
X		(void) putchar('\n');
X	}
X# endif DEBUG
X
X	do
X	{
X		/* read a token */
X		tok = q;
X		for (;;)
X		{
X			/* store away any old lookahead character */
X			if (c != NOCHAR)
X			{
X				/* see if there is room */
X				if (q >= &pvpbuf[PSBUFSIZE - 5])
X				{
X					usrerr("Address too long");
X					DelimChar = p;
X					return (NULL);
X				}
X
X				/* squirrel it away */
X				*q++ = c;
X			}
X
X			/* read a new input character */
X			c = *p++;
X			if (c == '\0')
X				break;
X			c &= ~0200;
X
X# ifdef DEBUG
X			if (tTd(22, 101))
X				printf("c=%c, s=%d; ", c, state);
X# endif DEBUG
X
X			/* chew up special characters */
X			*q = '\0';
X			if (bslashmode)
X			{
X				/* kludge \! for naive users */
X				if (c != '!')
X					c |= 0200;
X				bslashmode = FALSE;
X			}
X			else if (c == '\\')
X			{
X				bslashmode = TRUE;
X				c = NOCHAR;
X			}
X			else if (state == QST)
X			{
X				/* do nothing, just avoid next clauses */
X			}
X			else if (c == '(')
X			{
X				cmntcnt++;
X				c = NOCHAR;
X			}
X			else if (c == ')')
X			{
X				if (cmntcnt <= 0)
X				{
X					usrerr("Unbalanced ')'");
X					DelimChar = p;
X					return (NULL);
X				}
X				else
X					cmntcnt--;
X			}
X			else if (cmntcnt > 0)
X				c = NOCHAR;
X			else if (c == '<')
X				anglecnt++;
X			else if (c == '>')
X			{
X				if (anglecnt <= 0)
X				{
X					usrerr("Unbalanced '>'");
X					DelimChar = p;
X					return (NULL);
X				}
X				anglecnt--;
X			}
X			else if (delim == ' ' && isspace(c))
X				c = ' ';
X
X			if (c == NOCHAR)
X				continue;
X
X			/* see if this is end of input */
X			if (c == delim && anglecnt <= 0 && state != QST)
X				break;
X
X			newstate = StateTab[state][toktype(c)];
X# ifdef DEBUG
X			if (tTd(22, 101))
X				printf("ns=%02o\n", newstate);
X# endif DEBUG
X			state = newstate & TYPE;
X			if (bitset(M, newstate))
X				c = NOCHAR;
X			if (bitset(B, newstate))
X				break;
X		}
X
X		/* new token */
X		if (tok != q)
X		{
X			*q++ = '\0';
X# ifdef DEBUG
X			if (tTd(22, 36))
X			{
X				printf("tok=");
X				xputs(tok);
X				(void) putchar('\n');
X			}
X# endif DEBUG
X			if (avp >= &av[MAXATOM])
X			{
X				syserr("prescan: too many tokens");
X				DelimChar = p;
X				return (NULL);
X			}
X			*avp++ = tok;
X		}
X	} while (c != '\0' && (c != delim || anglecnt > 0));
X	*avp = NULL;
X	DelimChar = --p;
X	if (cmntcnt > 0)
X		usrerr("Unbalanced '('");
X	else if (anglecnt > 0)
X		usrerr("Unbalanced '<'");
X	else if (state == QST)
X		usrerr("Unbalanced '\"'");
X	else if (av[0] != NULL)
X		return (av);
X	return (NULL);
X}
X/*
X**  TOKTYPE -- return token type
X**
X**	Parameters:
X**		c -- the character in question.
X**
X**	Returns:
X**		Its type.
X**
X**	Side Effects:
X**		none.
X*/
X
Xtoktype(c)
X	register char c;
X{
X	static char buf[50];
X	static bool firstime = TRUE;
X
X	if (firstime)
X	{
X		firstime = FALSE;
X		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
X		(void) strcat(buf, DELIMCHARS);
X	}
X	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
X		return (ONE);
X	if (c == '"')
X		return (QST);
X	if (!isascii(c))
X		return (ATM);
X	if (isspace(c) || c == ')')
X		return (SPC);
X	if (iscntrl(c) || index(buf, c) != NULL)
X		return (OPR);
X	return (ATM);
X}
X/*
X**  REWRITE -- apply rewrite rules to token vector.
X**
X**	This routine is an ordered production system.  Each rewrite
X**	rule has a LHS (called the pattern) and a RHS (called the
X**	rewrite); 'rwr' points the the current rewrite rule.
X**
X**	For each rewrite rule, 'avp' points the address vector we
X**	are trying to match against, and 'pvp' points to the pattern.
X**	If pvp points to a special match value (MATCHZANY, MATCHANY,
X**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
X**	matched is saved away in the match vector (pointed to by 'mvp').
X**
X**	When a match between avp & pvp does not match, we try to
X**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
X**	we must also back out the match in mvp.  If we reach a
X**	MATCHANY or MATCHZANY we just extend the match and start
X**	over again.
X**
X**	When we finally match, we rewrite the address vector
X**	and try over again.
X**
X**	Parameters:
X**		pvp -- pointer to token vector.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		pvp is modified.
X*/
X
Xstruct match
X{
X	char	**first;	/* first token matched */
X	char	**last;		/* last token matched */
X};
X
X# define MAXMATCH	9	/* max params per rewrite */
X
X
Xrewrite(pvp, ruleset)
X	char **pvp;
X	int ruleset;
X{
X	register char *ap;		/* address pointer */
X	register char *rp;		/* rewrite pointer */
X	register char **avp;		/* address vector pointer */
X	register char **rvp;		/* rewrite vector pointer */
X	register struct match *mlp;	/* cur ptr into mlist */
X	register struct rewrite *rwr;	/* pointer to current rewrite rule */
X	struct match mlist[MAXMATCH];	/* stores match on LHS */
X	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
X	extern bool sameword();
X
X	if (OpMode == MD_TEST || tTd(21, 2))
X	{
X		printf("rewrite: ruleset %2d   input:", ruleset);
X		printav(pvp);
X	}
X	if (pvp == NULL)
X		return;
X
X	/*
X	**  Run through the list of rewrite rules, applying
X	**	any that match.
X	*/
X
X	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
X	{
X# ifdef DEBUG
X		if (tTd(21, 12))
X		{
X			printf("-----trying rule:");
X			printav(rwr->r_lhs);
X		}
X# endif DEBUG
X
X		/* try to match on this rule */
X		mlp = mlist;
X		rvp = rwr->r_lhs;
X		avp = pvp;
X		while ((ap = *avp) != NULL || *rvp != NULL)
X		{
X			rp = *rvp;
X# ifdef DEBUG
X			if (tTd(21, 35))
X			{
X				printf("ap=");
X				xputs(ap);
X				printf(", rp=");
X				xputs(rp);
X				printf("\n");
X			}
X# endif DEBUG
X			if (rp == NULL)
X			{
X				/* end-of-pattern before end-of-address */
X				goto backup;
X			}
X			if (ap == NULL && *rp != MATCHZANY)
X			{
X				/* end-of-input */
X				break;
X			}
X
X			switch (*rp)
X			{
X				register STAB *s;
X
X			  case MATCHCLASS:
X			  case MATCHNCLASS:
X				/* match any token in (not in) a class */
X				s = stab(ap, ST_CLASS, ST_FIND);
X				if (s == NULL || !bitnset(rp[1], s->s_class))
X				{
X					if (*rp == MATCHCLASS)
X						goto backup;
X				}
X				else if (*rp == MATCHNCLASS)
X					goto backup;
X
X				/* explicit fall-through */
X
X			  case MATCHONE:
X			  case MATCHANY:
X				/* match exactly one token */
X				mlp->first = avp;
X				mlp->last = avp++;
X				mlp++;
X				break;
X
X			  case MATCHZANY:
X				/* match zero or more tokens */
X				mlp->first = avp;
X				mlp->last = avp - 1;
X				mlp++;
X				break;
X
X			  default:
X				/* must have exact match */
X				if (!sameword(rp, ap))
X					goto backup;
X				avp++;
X				break;
X			}
X
X			/* successful match on this token */
X			rvp++;
X			continue;
X
X		  backup:
X			/* match failed -- back up */
X			while (--rvp >= rwr->r_lhs)
X			{
X				rp = *rvp;
X				if (*rp == MATCHANY || *rp == MATCHZANY)
X				{
X					/* extend binding and continue */
X					avp = ++mlp[-1].last;
X					avp++;
X					rvp++;
X					break;
X				}
X				avp--;
X				if (*rp == MATCHONE || *rp == MATCHCLASS ||
X				    *rp == MATCHNCLASS)
X				{
X					/* back out binding */
X					mlp--;
X				}
X			}
X
X			if (rvp < rwr->r_lhs)
X			{
X				/* total failure to match */
X				break;
X			}
X		}
X
X		/*
X		**  See if we successfully matched
X		*/
X
X		if (rvp < rwr->r_lhs || *rvp != NULL)
X		{
X# ifdef DEBUG
X			if (tTd(21, 10))
X				printf("----- rule fails\n");
X# endif DEBUG
X			rwr = rwr->r_next;
X			continue;
X		}
X
X		rvp = rwr->r_rhs;
X# ifdef DEBUG
X		if (tTd(21, 12))
X		{
X			printf("-----rule matches:");
X			printav(rvp);
X		}
X# endif DEBUG
X
X		rp = *rvp;
X		if (*rp == CANONUSER)
X		{
X			rvp++;
X			rwr = rwr->r_next;
X		}
X		else if (*rp == CANONHOST)
X		{
X			rvp++;
X			rwr = NULL;
X		}
X		else if (*rp == CANONNET)
X			rwr = NULL;
X
X		/* substitute */
X		for (avp = npvp; *rvp != NULL; rvp++)
X		{
X			register struct match *m;
X			register char **pp;
X
X			rp = *rvp;
X			if (*rp == MATCHREPL)
X			{
X				/* substitute from LHS */
X				m = &mlist[rp[1] - '1'];
X				if (m >= mlp)
X				{
X					syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
X					return;
X				}
X# ifdef DEBUG
X				if (tTd(21, 15))
X				{
X					printf("$%c:", rp[1]);
X					pp = m->first;
X					while (pp <= m->last)
X					{
X						printf(" %x=\"", *pp);
X						(void) fflush(stdout);
X						printf("%s\"", *pp++);
X					}
X					printf("\n");
X				}
X# endif DEBUG
X				pp = m->first;
X				while (pp <= m->last)
X				{
X					if (avp >= &npvp[MAXATOM])
X					{
X						syserr("rewrite: expansion too long");
X						return;
X					}
X					*avp++ = *pp++;
X				}
X			}
X			else
X			{
X				/* vanilla replacement */
X				if (avp >= &npvp[MAXATOM])
X				{
X	toolong:
X					syserr("rewrite: expansion too long");
X					return;
X				}
X				*avp++ = rp;
X			}
X		}
X		*avp++ = NULL;
X
X		/*
X		**  Check for any hostname lookups.
X		*/
X
X		for (rvp = npvp; *rvp != NULL; rvp++)
X		{
X			char **hbrvp;
X			char **xpvp;
X			int trsize;
X			char *olddelimchar;
X			char buf[MAXNAME + 1];
X			char *pvpb1[MAXATOM + 1];
X			char pvpbuf[PSBUFSIZE];
X			extern char *DelimChar;
X
X			if (**rvp != HOSTBEGIN)
X				continue;
X
X			/*
X			**  Got a hostname lookup.
X			**
X			**	This could be optimized fairly easily.
X			*/
X
X			hbrvp = rvp;
X
X			/* extract the match part */
X			while (*++rvp != NULL && **rvp != HOSTEND)
X				continue;
X			if (*rvp != NULL)
X				*rvp++ = NULL;
X
X			/* save the remainder of the input string */
X			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
X			bcopy((char *) rvp, (char *) pvpb1, trsize);
X
X			/* look it up */
X			cataddr(++hbrvp, buf, sizeof buf);
X			maphostname(buf, sizeof buf);
X
X			/* scan the new host name */
X			olddelimchar = DelimChar;
X			xpvp = prescan(buf, '\0', pvpbuf);
X			DelimChar = olddelimchar;
X			if (xpvp == NULL)
X			{
X				syserr("rewrite: cannot prescan canonical hostname: %s", buf);
X				return;
X			}
X
X			/* append it to the token list */
X			for (avp = --hbrvp; *xpvp != NULL; xpvp++)
X			{
X				*avp++ = newstr(*xpvp);
X				if (avp >= &npvp[MAXATOM])
X					goto toolong;
X			}
X
X			/* restore the old trailing information */
X			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
X				if (avp >= &npvp[MAXATOM])
X					goto toolong;
X
X			break;
X		}
X
X		/*
X		**  Check for subroutine calls.
X		*/
X
X		if (*npvp != NULL && **npvp == CALLSUBR)
X		{
X			bcopy((char *) &npvp[2], (char *) pvp,
X				(int) (avp - npvp - 2) * sizeof *avp);
X# ifdef DEBUG
X			if (tTd(21, 3))
X				printf("-----callsubr %s\n", npvp[1]);
X# endif DEBUG
X			rewrite(pvp, atoi(npvp[1]));
X		}
X		else
X		{
X			bcopy((char *) npvp, (char *) pvp,
X				(int) (avp - npvp) * sizeof *avp);
X		}
X# ifdef DEBUG
X		if (tTd(21, 4))
X		{
X			printf("rewritten as:");
X			printav(pvp);
X		}
X# endif DEBUG
X	}
X
X	if (OpMode == MD_TEST || tTd(21, 2))
X	{
X		printf("rewrite: ruleset %2d returns:", ruleset);
X		printav(pvp);
X	}
X}
X/*
X**  BUILDADDR -- build address from token vector.
X**
X**	Parameters:
X**		tv -- token vector.
X**		a -- pointer to address descriptor to fill.
X**			If NULL, one will be allocated.
X**
X**	Returns:
X**		NULL if there was an error.
X**		'a' otherwise.
X**
X**	Side Effects:
X**		fills in 'a'
X*/
X
XADDRESS *
Xbuildaddr(tv, a)
X	register char **tv;
X	register ADDRESS *a;
X{
X	static char buf[MAXNAME];
X	struct mailer **mp;
X	register struct mailer *m;
X	extern bool sameword();
X
X	if (a == NULL)
X		a = (ADDRESS *) xalloc(sizeof *a);
X	bzero((char *) a, sizeof *a);
X
X	/* figure out what net/mailer to use */
X	if (**tv != CANONNET)
X	{
X		syserr("buildaddr: no net");
X		return (NULL);
X	}
X	tv++;
X	if (sameword(*tv, "error"))
X	{
X		if (**++tv == CANONHOST)
X		{
X			setstat(atoi(*++tv));
X			tv++;
X		}
X		if (**tv != CANONUSER)
X			syserr("buildaddr: error: no user");
X		buf[0] = '\0';
X		while (*++tv != NULL)
X		{
X			if (buf[0] != '\0')
X				(void) strcat(buf, " ");
X			(void) strcat(buf, *tv);
X		}
X		usrerr(buf);
X		return (NULL);
X	}
X	for (mp = Mailer; (m = *mp++) != NULL; )
X	{
X		if (sameword(m->m_name, *tv))
X			break;
X	}
X	if (m == NULL)
X	{
X		syserr("buildaddr: unknown mailer %s", *tv);
X		return (NULL);
X	}
X	a->q_mailer = m;
X
X	/* figure out what host (if any) */
X	tv++;
X	if (!bitnset(M_LOCAL, m->m_flags))
X	{
X		if (**tv++ != CANONHOST)
X		{
X			syserr("buildaddr: no host");
X			return (NULL);
X		}
X		buf[0] = '\0';
X		while (*tv != NULL && **tv != CANONUSER)
X			(void) strcat(buf, *tv++);
X		a->q_host = newstr(buf);
X	}
X	else
X		a->q_host = NULL;
X
X	/* figure out the user */
X	if (**tv != CANONUSER)
X	{
X		syserr("buildaddr: no user");
X		return (NULL);
X	}
X
X	/* rewrite according recipient mailer rewriting rules */
X	rewrite(++tv, 2);
X	if (m->m_r_rwset > 0)
X		rewrite(tv, m->m_r_rwset);
X	rewrite(tv, 4);
X
X	/* save the result for the command line/RCPT argument */
X	cataddr(tv, buf, sizeof buf);
X	a->q_user = buf;
X
X	return (a);
X}
X/*
X**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
X**
X**	Parameters:
X**		pvp -- parameter vector to rebuild.
X**		buf -- buffer to build the string into.
X**		sz -- size of buf.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Destroys buf.
X*/
X
Xcataddr(pvp, buf, sz)
X	char **pvp;
X	char *buf;
X	register int sz;
X{
X	bool oatomtok = FALSE;
X	bool natomtok = FALSE;
X	register int i;
X	register char *p;
X
X	if (pvp == NULL)
X	{
X		(void) strcpy(buf, "");
X		return;
X	}
X	p = buf;
X	sz -= 2;
X	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
X	{
X		natomtok = (toktype(**pvp) == ATM);
X		if (oatomtok && natomtok)
X			*p++ = SpaceSub;
X		(void) strcpy(p, *pvp);
X		oatomtok = natomtok;
X		p += i;
X		sz -= i + 1;
X		pvp++;
X	}
X	*p = '\0';
X}
X/*
X**  SAMEADDR -- Determine if two addresses are the same
X**
X**	This is not just a straight comparison -- if the mailer doesn't
X**	care about the host we just ignore it, etc.
X**
X**	Parameters:
X**		a, b -- pointers to the internal forms to compare.
X**
X**	Returns:
X**		TRUE -- they represent the same mailbox.
X**		FALSE -- they don't.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xsameaddr(a, b)
X	register ADDRESS *a;
X	register ADDRESS *b;
X{
X	/* if they don't have the same mailer, forget it */
X	if (a->q_mailer != b->q_mailer)
X		return (FALSE);
X
X	/* if the user isn't the same, we can drop out */
X	if (strcmp(a->q_user, b->q_user) != 0)
X		return (FALSE);
X
X	/* if the mailer ignores hosts, we have succeeded! */
X	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
X		return (TRUE);
X
X	/* otherwise compare hosts (but be careful for NULL ptrs) */
X	if (a->q_host == NULL || b->q_host == NULL)
X		return (FALSE);
X	if (strcmp(a->q_host, b->q_host) != 0)
X		return (FALSE);
X
X	return (TRUE);
X}
X/*
X**  PRINTADDR -- print address (for debugging)
X**
X**	Parameters:
X**		a -- the address to print
X**		follow -- follow the q_next chain.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
X# ifdef DEBUG
X
Xprintaddr(a, follow)
X	register ADDRESS *a;
X	bool follow;
X{
X	bool first = TRUE;
X
X	while (a != NULL)
X	{
X		first = FALSE;
X		printf("%x=", a);
X		(void) fflush(stdout);
X		printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
X		       a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host,
X		       a->q_user);
X		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
X		       a->q_alias);
X		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
X		       a->q_fullname);
X
X		if (!follow)
X			return;
X		a = a->q_next;
X	}
X	if (first)
X		printf("[NULL]\n");
X}
X
X# endif DEBUG
X/*
X**  REMOTENAME -- return the name relative to the current mailer
X**
X**	Parameters:
X**		name -- the name to translate.
X**		m -- the mailer that we want to do rewriting relative
X**			to.
X**		senderaddress -- if set, uses the sender rewriting rules
X**			rather than the recipient rewriting rules.
X**		canonical -- if set, strip out any comment information,
X**			etc.
X**
X**	Returns:
X**		the text string representing this address relative to
X**			the receiving mailer.
X**
X**	Side Effects:
X**		none.
X**
X**	Warnings:
X**		The text string returned is tucked away locally;
X**			copy it if you intend to save it.
X*/
X
Xchar *
Xremotename(name, m, senderaddress, canonical)
X	char *name;
X	struct mailer *m;
X	bool senderaddress;
X	bool canonical;
X{
X	register char **pvp;
X	char *fancy;
X	extern char *macvalue();
X	char *oldg = macvalue('g', CurEnv);
X	static char buf[MAXNAME];
X	char lbuf[MAXNAME];
X	char pvpbuf[PSBUFSIZE];
X	extern char **prescan();
X	extern char *crackaddr();
X
X# ifdef DEBUG
X	if (tTd(12, 1))
X		printf("remotename(%s)\n", name);
X# endif DEBUG
X
X	/* don't do anything if we are tagging it as special */
X	if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
X		return (name);
X
X	/*
X	**  Do a heuristic crack of this name to extract any comment info.
X	**	This will leave the name as a comment and a $g macro.
X	*/
X
X	if (canonical)
X		fancy = "\001g";
X	else
X		fancy = crackaddr(name);
X
X	/*
X	**  Turn the name into canonical form.
X	**	Normally this will be RFC 822 style, i.e., "user at domain".
X	**	If this only resolves to "user", and the "C" flag is
X	**	specified in the sending mailer, then the sender's
X	**	domain will be appended.
X	*/
X
X	pvp = prescan(name, '\0', pvpbuf);
X	if (pvp == NULL)
X		return (name);
X	rewrite(pvp, 3);
X	if (CurEnv->e_fromdomain != NULL)
X	{
X		/* append from domain to this address */
X		register char **pxp = pvp;
X
X		/* see if there is an "@domain" in the current name */
X		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
X			pxp++;
X		if (*pxp == NULL)
X		{
X			/* no.... append the "@domain" from the sender */
X			register char **qxq = CurEnv->e_fromdomain;
X
X			while ((*pxp++ = *qxq++) != NULL)
X				continue;
X			rewrite(pvp, 3);
X		}
X	}
X
X	/*
X	**  Do more specific rewriting.
X	**	Rewrite using ruleset 1 or 2 depending on whether this is
X	**		a sender address or not.
X	**	Then run it through any receiving-mailer-specific rulesets.
X	*/
X
X	if (senderaddress)
X	{
X		rewrite(pvp, 1);
X		if (m->m_s_rwset > 0)
X			rewrite(pvp, m->m_s_rwset);
X	}
X	else
X	{
X		rewrite(pvp, 2);
X		if (m->m_r_rwset > 0)
X			rewrite(pvp, m->m_r_rwset);
X	}
X
X	/*
X	**  Do any final sanitation the address may require.
X	**	This will normally be used to turn internal forms
X	**	(e.g., user at host.LOCAL) into external form.  This
X	**	may be used as a default to the above rules.
X	*/
X
X	rewrite(pvp, 4);
X
X	/*
X	**  Now restore the comment information we had at the beginning.
X	*/
X
X	cataddr(pvp, lbuf, sizeof lbuf);
X	define('g', lbuf, CurEnv);
X	expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
X	define('g', oldg, CurEnv);
X
X# ifdef DEBUG
X	if (tTd(12, 1))
X		printf("remotename => `%s'\n", buf);
X# endif DEBUG
X	return (buf);
X}
END_OF_FILE
if test 24032 -ne `wc -c <'src/parseaddr.c'`; then
    echo shar: \"'src/parseaddr.c'\" unpacked with wrong size!
fi
# end of 'src/parseaddr.c'
fi
echo shar: End of archive 7 \(of 8\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave at galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb at rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb



More information about the Unix-pc.sources mailing list