parseaddr.c revision 132943
138032Speter/*
2111823Sgshapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1538032Speter
16132943SgshapiroSM_RCSID("@(#)$Id: parseaddr.c,v 8.378 2004/05/18 20:01:54 ca Exp $")
1790792Sgshapiro
1890792Sgshapirostatic void	allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
1964562Sgshapirostatic int	callsubr __P((char**, int, ENVELOPE *));
2064562Sgshapirostatic char	*map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
2164562Sgshapirostatic ADDRESS	*buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
2294334Sgshapirostatic bool	hasctrlchar __P((register char *, bool, bool));
2364562Sgshapiro
2494334Sgshapiro/* replacement for illegal characters in addresses */
2594334Sgshapiro#define BAD_CHAR_REPLACEMENT	'?'
2694334Sgshapiro
2738032Speter/*
2838032Speter**  PARSEADDR -- Parse an address
2938032Speter**
3038032Speter**	Parses an address and breaks it up into three parts: a
3138032Speter**	net to transmit the message on, the host to transmit it
3238032Speter**	to, and a user on that host.  These are loaded into an
3338032Speter**	ADDRESS header with the values squirreled away if necessary.
3438032Speter**	The "user" part may not be a real user; the process may
3538032Speter**	just reoccur on that machine.  For example, on a machine
3638032Speter**	with an arpanet connection, the address
3738032Speter**		csvax.bill@berkeley
3838032Speter**	will break up to a "user" of 'csvax.bill' and a host
3938032Speter**	of 'berkeley' -- to be transmitted over the arpanet.
4038032Speter**
4138032Speter**	Parameters:
4238032Speter**		addr -- the address to parse.
4338032Speter**		a -- a pointer to the address descriptor buffer.
4490792Sgshapiro**			If NULL, an address will be created.
4538032Speter**		flags -- describe detail for parsing.  See RF_ definitions
4638032Speter**			in sendmail.h.
4738032Speter**		delim -- the character to terminate the address, passed
4838032Speter**			to prescan.
4938032Speter**		delimptr -- if non-NULL, set to the location of the
5038032Speter**			delim character that was found.
5138032Speter**		e -- the envelope that will contain this address.
5290792Sgshapiro**		isrcpt -- true if the address denotes a recipient; false
5390792Sgshapiro**			indicates a sender.
5438032Speter**
5538032Speter**	Returns:
5638032Speter**		A pointer to the address descriptor header (`a' if
5738032Speter**			`a' is non-NULL).
5838032Speter**		NULL on error.
5938032Speter**
6038032Speter**	Side Effects:
6190792Sgshapiro**		e->e_to = addr
6238032Speter*/
6338032Speter
6438032Speter/* following delimiters are inherent to the internal algorithms */
6564562Sgshapiro#define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */
6638032Speter
6738032SpeterADDRESS *
6890792Sgshapiroparseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
6938032Speter	char *addr;
7038032Speter	register ADDRESS *a;
7138032Speter	int flags;
7238032Speter	int delim;
7338032Speter	char **delimptr;
7438032Speter	register ENVELOPE *e;
7590792Sgshapiro	bool isrcpt;
7638032Speter{
7790792Sgshapiro	char **pvp;
7838032Speter	auto char *delimptrbuf;
7964562Sgshapiro	bool qup;
8038032Speter	char pvpbuf[PSBUFSIZE];
8138032Speter
8238032Speter	/*
8338032Speter	**  Initialize and prescan address.
8438032Speter	*/
8538032Speter
8638032Speter	e->e_to = addr;
8738032Speter	if (tTd(20, 1))
8890792Sgshapiro		sm_dprintf("\n--parseaddr(%s)\n", addr);
8938032Speter
9038032Speter	if (delimptr == NULL)
9138032Speter		delimptr = &delimptrbuf;
9238032Speter
93132943Sgshapiro	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL, false);
9438032Speter	if (pvp == NULL)
9538032Speter	{
9638032Speter		if (tTd(20, 1))
9790792Sgshapiro			sm_dprintf("parseaddr-->NULL\n");
9864562Sgshapiro		return NULL;
9938032Speter	}
10038032Speter
10190792Sgshapiro	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))
10238032Speter	{
10338032Speter		if (tTd(20, 1))
10490792Sgshapiro			sm_dprintf("parseaddr-->bad address\n");
10538032Speter		return NULL;
10638032Speter	}
10738032Speter
10838032Speter	/*
10938032Speter	**  Save addr if we are going to have to.
11038032Speter	**
11138032Speter	**	We have to do this early because there is a chance that
11238032Speter	**	the map lookups in the rewriting rules could clobber
11338032Speter	**	static memory somewhere.
11438032Speter	*/
11538032Speter
11638032Speter	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
11738032Speter	{
11838032Speter		char savec = **delimptr;
11938032Speter
12038032Speter		if (savec != '\0')
12138032Speter			**delimptr = '\0';
12290792Sgshapiro		e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);
12338032Speter		if (savec != '\0')
12438032Speter			**delimptr = savec;
12538032Speter	}
12638032Speter
12738032Speter	/*
12838032Speter	**  Apply rewriting rules.
12938032Speter	**	Ruleset 0 does basic parsing.  It must resolve.
13038032Speter	*/
13138032Speter
13290792Sgshapiro	qup = false;
13390792Sgshapiro	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
13490792Sgshapiro		qup = true;
13590792Sgshapiro	if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)
13690792Sgshapiro		qup = true;
13738032Speter
13838032Speter	/*
13938032Speter	**  Build canonical address from pvp.
14038032Speter	*/
14138032Speter
14238032Speter	a = buildaddr(pvp, a, flags, e);
14338032Speter
14494334Sgshapiro	if (hasctrlchar(a->q_user, isrcpt, true))
14590792Sgshapiro	{
14690792Sgshapiro		if (tTd(20, 1))
14790792Sgshapiro			sm_dprintf("parseaddr-->bad q_user\n");
14894334Sgshapiro
14994334Sgshapiro		/*
15094334Sgshapiro		**  Just mark the address as bad so DSNs work.
15194334Sgshapiro		**  hasctrlchar() has to make sure that the address
15294334Sgshapiro		**  has been sanitized, e.g., shortened.
15394334Sgshapiro		*/
15494334Sgshapiro
15594334Sgshapiro		a->q_state = QS_BADADDR;
15690792Sgshapiro	}
15790792Sgshapiro
15838032Speter	/*
15938032Speter	**  Make local copies of the host & user and then
16038032Speter	**  transport them out.
16138032Speter	*/
16238032Speter
16390792Sgshapiro	allocaddr(a, flags, addr, e);
16464562Sgshapiro	if (QS_IS_BADADDR(a->q_state))
16594334Sgshapiro	{
16694334Sgshapiro		/* weed out bad characters in the printable address too */
16794334Sgshapiro		(void) hasctrlchar(a->q_paddr, isrcpt, false);
16838032Speter		return a;
16994334Sgshapiro	}
17038032Speter
17138032Speter	/*
17290792Sgshapiro	**  Select a queue directory for recipient addresses.
17390792Sgshapiro	**	This is done here and in split_across_queue_groups(),
17490792Sgshapiro	**	but the latter applies to addresses after aliasing,
17590792Sgshapiro	**	and only if splitting is done.
17690792Sgshapiro	*/
17790792Sgshapiro
17890792Sgshapiro	if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&
17990792Sgshapiro	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags) &&
18090792Sgshapiro	    OpMode != MD_INITALIAS)
18190792Sgshapiro	{
18290792Sgshapiro		int r;
18390792Sgshapiro
18490792Sgshapiro		/* call ruleset which should return a queue group name */
18590792Sgshapiro		r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,
18690792Sgshapiro			  sizeof(pvpbuf));
18790792Sgshapiro		if (r == EX_OK &&
18890792Sgshapiro		    pvp != NULL && pvp[0] != NULL &&
18990792Sgshapiro		    (pvp[0][0] & 0377) == CANONNET &&
19090792Sgshapiro		    pvp[1] != NULL && pvp[1][0] != '\0')
19190792Sgshapiro		{
19290792Sgshapiro			r = name2qid(pvp[1]);
19390792Sgshapiro			if (r == NOQGRP && LogLevel > 10)
19490792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
19590792Sgshapiro					"can't find queue group name %s, selection ignored",
19690792Sgshapiro					pvp[1]);
19790792Sgshapiro			if (tTd(20, 4) && r != NOQGRP)
19890792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
19990792Sgshapiro					"queue group name %s -> %d",
20090792Sgshapiro					pvp[1], r);
20190792Sgshapiro			a->q_qgrp = r == NOQGRP ? ENVQGRP : r;
20290792Sgshapiro		}
20390792Sgshapiro	}
20490792Sgshapiro
20590792Sgshapiro	/*
20638032Speter	**  If there was a parsing failure, mark it for queueing.
20738032Speter	*/
20838032Speter
20964562Sgshapiro	if (qup && OpMode != MD_INITALIAS)
21038032Speter	{
21138032Speter		char *msg = "Transient parse error -- message queued for future delivery";
21238032Speter
21338032Speter		if (e->e_sendmode == SM_DEFER)
21438032Speter			msg = "Deferring message until queue run";
21538032Speter		if (tTd(20, 1))
21690792Sgshapiro			sm_dprintf("parseaddr: queuing message\n");
21738032Speter		message(msg);
21838032Speter		if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
21990792Sgshapiro			e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);
22064562Sgshapiro		a->q_state = QS_QUEUEUP;
22138032Speter		a->q_status = "4.4.3";
22238032Speter	}
22338032Speter
22438032Speter	/*
22538032Speter	**  Compute return value.
22638032Speter	*/
22738032Speter
22838032Speter	if (tTd(20, 1))
22938032Speter	{
23090792Sgshapiro		sm_dprintf("parseaddr-->");
231132943Sgshapiro		printaddr(sm_debug_file(), a, false);
23238032Speter	}
23338032Speter
23464562Sgshapiro	return a;
23538032Speter}
23690792Sgshapiro/*
23790792Sgshapiro**  INVALIDADDR -- check for address containing characters used for macros
23838032Speter**
23938032Speter**	Parameters:
24038032Speter**		addr -- the address to check.
24190792Sgshapiro**		delimptr -- if non-NULL: end of address to check, i.e.,
24290792Sgshapiro**			a pointer in the address string.
24390792Sgshapiro**		isrcpt -- true iff the address is for a recipient.
24438032Speter**
24538032Speter**	Returns:
24690792Sgshapiro**		true -- if the address has characters that are reservered
24790792Sgshapiro**			for macros or is too long.
24890792Sgshapiro**		false -- otherwise.
24938032Speter*/
25038032Speter
25138032Speterbool
25290792Sgshapiroinvalidaddr(addr, delimptr, isrcpt)
25338032Speter	register char *addr;
25438032Speter	char *delimptr;
25590792Sgshapiro	bool isrcpt;
25638032Speter{
25790792Sgshapiro	bool result = false;
25838032Speter	char savedelim = '\0';
25994334Sgshapiro	char *b = addr;
26090792Sgshapiro	int len = 0;
26138032Speter
26238032Speter	if (delimptr != NULL)
26338032Speter	{
26490792Sgshapiro		/* delimptr points to the end of the address to test */
26538032Speter		savedelim = *delimptr;
26690792Sgshapiro		if (savedelim != '\0')	/* if that isn't '\0' already: */
26790792Sgshapiro			*delimptr = '\0';	/* set it */
26838032Speter	}
26990792Sgshapiro	for (; *addr != '\0'; addr++)
27038032Speter	{
27190792Sgshapiro		if ((*addr & 0340) == 0200)
27290792Sgshapiro		{
27390792Sgshapiro			setstat(EX_USAGE);
27490792Sgshapiro			result = true;
27594334Sgshapiro			*addr = BAD_CHAR_REPLACEMENT;
27690792Sgshapiro		}
27790792Sgshapiro		if (++len > MAXNAME - 1)
27890792Sgshapiro		{
27994334Sgshapiro			char saved = *addr;
28094334Sgshapiro
28194334Sgshapiro			*addr = '\0';
28294334Sgshapiro			usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
28394334Sgshapiro			       b, MAXNAME - 1);
28494334Sgshapiro			*addr = saved;
28590792Sgshapiro			result = true;
28690792Sgshapiro			goto delim;
28790792Sgshapiro		}
28838032Speter	}
28990792Sgshapiro	if (result)
29090792Sgshapiro	{
29190792Sgshapiro		if (isrcpt)
29294334Sgshapiro			usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"",
29394334Sgshapiro			       b);
29490792Sgshapiro		else
29594334Sgshapiro			usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"",
29694334Sgshapiro			       b);
29790792Sgshapiro	}
29890792Sgshapirodelim:
29990792Sgshapiro	if (delimptr != NULL && savedelim != '\0')
30090792Sgshapiro		*delimptr = savedelim;	/* restore old character at delimptr */
30190792Sgshapiro	return result;
30290792Sgshapiro}
30390792Sgshapiro/*
30490792Sgshapiro**  HASCTRLCHAR -- check for address containing meta-characters
30590792Sgshapiro**
30690792Sgshapiro**  Checks that the address contains no meta-characters, and contains
30790792Sgshapiro**  no "non-printable" characters unless they are quoted or escaped.
30890792Sgshapiro**  Quoted or escaped characters are literals.
30990792Sgshapiro**
31090792Sgshapiro**	Parameters:
31190792Sgshapiro**		addr -- the address to check.
31290792Sgshapiro**		isrcpt -- true if the address is for a recipient; false
31390792Sgshapiro**			indicates a from.
31494334Sgshapiro**		complain -- true if an error should issued if the address
31594334Sgshapiro**			is invalid and should be "repaired".
31690792Sgshapiro**
31790792Sgshapiro**	Returns:
31890792Sgshapiro**		true -- if the address has any "wierd" characters or
31990792Sgshapiro**			non-printable characters or if a quote is unbalanced.
32090792Sgshapiro**		false -- otherwise.
32190792Sgshapiro*/
32290792Sgshapiro
32390792Sgshapirostatic bool
32494334Sgshapirohasctrlchar(addr, isrcpt, complain)
32590792Sgshapiro	register char *addr;
32694334Sgshapiro	bool isrcpt, complain;
32790792Sgshapiro{
32894334Sgshapiro	bool quoted = false;
32990792Sgshapiro	int len = 0;
33094334Sgshapiro	char *result = NULL;
33194334Sgshapiro	char *b = addr;
33290792Sgshapiro
33390792Sgshapiro	if (addr == NULL)
33490792Sgshapiro		return false;
33538032Speter	for (; *addr != '\0'; addr++)
33638032Speter	{
33794334Sgshapiro		if (++len > MAXNAME - 1)
33894334Sgshapiro		{
33994334Sgshapiro			if (complain)
34094334Sgshapiro			{
34194334Sgshapiro				(void) shorten_rfc822_string(b, MAXNAME - 1);
34294334Sgshapiro				usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
34394334Sgshapiro				       b, MAXNAME - 1);
34494334Sgshapiro				return true;
34594334Sgshapiro			}
34694334Sgshapiro			result = "too long";
34794334Sgshapiro		}
34890792Sgshapiro		if (!quoted && (*addr < 32 || *addr == 127))
34990792Sgshapiro		{
35094334Sgshapiro			result = "non-printable character";
35194334Sgshapiro			*addr = BAD_CHAR_REPLACEMENT;
35294334Sgshapiro			continue;
35390792Sgshapiro		}
35490792Sgshapiro		if (*addr == '"')
35590792Sgshapiro			quoted = !quoted;
35690792Sgshapiro		else if (*addr == '\\')
35790792Sgshapiro		{
35890792Sgshapiro			/* XXX Generic problem: no '\0' in strings. */
35990792Sgshapiro			if (*++addr == '\0')
36090792Sgshapiro			{
36194334Sgshapiro				result = "trailing \\ character";
36294334Sgshapiro				*--addr = BAD_CHAR_REPLACEMENT;
36390792Sgshapiro				break;
36490792Sgshapiro			}
36590792Sgshapiro		}
36638032Speter		if ((*addr & 0340) == 0200)
36790792Sgshapiro		{
36890792Sgshapiro			setstat(EX_USAGE);
36994334Sgshapiro			result = "8-bit character";
37094334Sgshapiro			*addr = BAD_CHAR_REPLACEMENT;
37194334Sgshapiro			continue;
37290792Sgshapiro		}
37338032Speter	}
37490792Sgshapiro	if (quoted)
37594334Sgshapiro		result = "unbalanced quote"; /* unbalanced quote */
37694334Sgshapiro	if (result != NULL && complain)
37738032Speter	{
37890792Sgshapiro		if (isrcpt)
37994334Sgshapiro			usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)",
38094334Sgshapiro			       b, result);
38190792Sgshapiro		else
38294334Sgshapiro			usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)",
38394334Sgshapiro			       b, result);
38438032Speter	}
38594334Sgshapiro	return result != NULL;
38638032Speter}
38790792Sgshapiro/*
38838032Speter**  ALLOCADDR -- do local allocations of address on demand.
38938032Speter**
39038032Speter**	Also lowercases the host name if requested.
39138032Speter**
39238032Speter**	Parameters:
39338032Speter**		a -- the address to reallocate.
39438032Speter**		flags -- the copy flag (see RF_ definitions in sendmail.h
39538032Speter**			for a description).
39638032Speter**		paddr -- the printname of the address.
39790792Sgshapiro**		e -- envelope
39838032Speter**
39938032Speter**	Returns:
40038032Speter**		none.
40138032Speter**
40238032Speter**	Side Effects:
40338032Speter**		Copies portions of a into local buffers as requested.
40438032Speter*/
40538032Speter
40664562Sgshapirostatic void
40790792Sgshapiroallocaddr(a, flags, paddr, e)
40838032Speter	register ADDRESS *a;
40938032Speter	int flags;
41038032Speter	char *paddr;
41190792Sgshapiro	ENVELOPE *e;
41238032Speter{
41338032Speter	if (tTd(24, 4))
41490792Sgshapiro		sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
41538032Speter
41638032Speter	a->q_paddr = paddr;
41738032Speter
41838032Speter	if (a->q_user == NULL)
41990792Sgshapiro		a->q_user = "";
42038032Speter	if (a->q_host == NULL)
42190792Sgshapiro		a->q_host = "";
42238032Speter
42338032Speter	if (bitset(RF_COPYPARSE, flags))
42438032Speter	{
42590792Sgshapiro		a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);
42638032Speter		if (a->q_user != a->q_paddr)
42790792Sgshapiro			a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);
42838032Speter	}
42938032Speter
43038032Speter	if (a->q_paddr == NULL)
43190792Sgshapiro		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
43290792Sgshapiro	a->q_qgrp = NOAQGRP;
43338032Speter}
43490792Sgshapiro/*
43538032Speter**  PRESCAN -- Prescan name and make it canonical
43638032Speter**
43738032Speter**	Scans a name and turns it into a set of tokens.  This process
43864562Sgshapiro**	deletes blanks and comments (in parentheses) (if the token type
43964562Sgshapiro**	for left paren is SPC).
44038032Speter**
44138032Speter**	This routine knows about quoted strings and angle brackets.
44238032Speter**
44338032Speter**	There are certain subtleties to this routine.  The one that
44438032Speter**	comes to mind now is that backslashes on the ends of names
44538032Speter**	are silently stripped off; this is intentional.  The problem
44638032Speter**	is that some versions of sndmsg (like at LBL) set the kill
44738032Speter**	character to something other than @ when reading addresses;
44838032Speter**	so people type "csvax.eric\@berkeley" -- which screws up the
44938032Speter**	berknet mailer.
45038032Speter**
45138032Speter**	Parameters:
45238032Speter**		addr -- the name to chomp.
45338032Speter**		delim -- the delimiter for the address, normally
45438032Speter**			'\0' or ','; \0 is accepted in any case.
45538032Speter**			If '\t' then we are reading the .cf file.
45638032Speter**		pvpbuf -- place to put the saved text -- note that
45738032Speter**			the pointers are static.
45838032Speter**		pvpbsize -- size of pvpbuf.
45938032Speter**		delimptr -- if non-NULL, set to the location of the
46038032Speter**			terminating delimiter.
46138032Speter**		toktab -- if set, a token table to use for parsing.
46238032Speter**			If NULL, use the default table.
463132943Sgshapiro**		ignore -- if true, ignore unbalanced addresses
46438032Speter**
46538032Speter**	Returns:
46638032Speter**		A pointer to a vector of tokens.
46738032Speter**		NULL on error.
46838032Speter*/
46938032Speter
47038032Speter/* states and character types */
47164562Sgshapiro#define OPR		0	/* operator */
47264562Sgshapiro#define ATM		1	/* atom */
47364562Sgshapiro#define QST		2	/* in quoted string */
47464562Sgshapiro#define SPC		3	/* chewing up spaces */
47564562Sgshapiro#define ONE		4	/* pick up one character */
47664562Sgshapiro#define ILL		5	/* illegal character */
47738032Speter
47864562Sgshapiro#define NSTATES	6	/* number of states */
47964562Sgshapiro#define TYPE		017	/* mask to select state type */
48038032Speter
48138032Speter/* meta bits for table */
48264562Sgshapiro#define M		020	/* meta character; don't pass through */
48364562Sgshapiro#define B		040	/* cause a break */
48464562Sgshapiro#define MB		M|B	/* meta-break */
48538032Speter
48638032Speterstatic short StateTab[NSTATES][NSTATES] =
48738032Speter{
48838032Speter   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
48938032Speter	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
49038032Speter	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
49138032Speter	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},
49238032Speter	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},
49338032Speter	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},
49438032Speter	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},
49538032Speter};
49638032Speter
49738032Speter/* token type table -- it gets modified with $o characters */
49890792Sgshapirostatic unsigned char	TokTypeTab[256] =
49938032Speter{
50038032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
50138032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
50238032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
50338032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
50438032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
50564562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
50638032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
50738032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
50838032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
50938032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51038032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
51138032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51238032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
51338032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51438032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
51538032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51638032Speter
51738032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
51838032Speter	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
51938032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
52038032Speter	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
52138032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
52238032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52338032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
52438032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52538032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
52638032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52738032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
52838032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52938032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
53038032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
53138032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
53238032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
53338032Speter};
53438032Speter
53538032Speter/* token type table for MIME parsing */
53690792Sgshapirounsigned char	MimeTokenTab[256] =
53738032Speter{
53838032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
53938032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
54038032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
54138032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
54238032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
54364562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
54438032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
54538032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
54638032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
54738032Speter	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
54838032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
54938032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
55038032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
55138032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
55238032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
55338032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
55438032Speter
55538032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
55638032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
55738032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
55838032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
55938032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
56038032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56138032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
56238032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56338032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
56438032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56538032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
56638032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56738032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
56838032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56938032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
57038032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
57138032Speter};
57238032Speter
57364562Sgshapiro/* token type table: don't strip comments */
57490792Sgshapirounsigned char	TokTypeNoC[256] =
57564562Sgshapiro{
57664562Sgshapiro    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
57764562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
57864562Sgshapiro    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
57964562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58064562Sgshapiro    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
58164562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
58264562Sgshapiro    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
58364562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58464562Sgshapiro    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
58564562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58664562Sgshapiro    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
58764562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58864562Sgshapiro    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
58964562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
59064562Sgshapiro    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
59164562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
59238032Speter
59364562Sgshapiro    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
59464562Sgshapiro	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
59564562Sgshapiro    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
59664562Sgshapiro	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
59764562Sgshapiro    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
59864562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
59964562Sgshapiro    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
60064562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60164562Sgshapiro    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
60264562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60364562Sgshapiro    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
60464562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60564562Sgshapiro    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
60664562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60764562Sgshapiro    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
60864562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60964562Sgshapiro};
61038032Speter
61164562Sgshapiro
612112810Sgshapiro#define NOCHAR		(-1)	/* signal nothing in lookahead token */
61364562Sgshapiro
61438032Speterchar **
615132943Sgshapiroprescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
61638032Speter	char *addr;
61738032Speter	int delim;
61838032Speter	char pvpbuf[];
61938032Speter	int pvpbsize;
62038032Speter	char **delimptr;
62190792Sgshapiro	unsigned char *toktab;
622132943Sgshapiro	bool ignore;
62338032Speter{
62438032Speter	register char *p;
62538032Speter	register char *q;
62638032Speter	register int c;
62738032Speter	char **avp;
62838032Speter	bool bslashmode;
62938032Speter	bool route_syntax;
63038032Speter	int cmntcnt;
63138032Speter	int anglecnt;
63238032Speter	char *tok;
63338032Speter	int state;
63438032Speter	int newstate;
63538032Speter	char *saveto = CurEnv->e_to;
63664562Sgshapiro	static char *av[MAXATOM + 1];
63790792Sgshapiro	static bool firsttime = true;
63838032Speter
63938032Speter	if (firsttime)
64038032Speter	{
64138032Speter		/* initialize the token type table */
64238032Speter		char obuf[50];
64338032Speter
64490792Sgshapiro		firsttime = false;
64538032Speter		if (OperatorChars == NULL)
64638032Speter		{
64738032Speter			if (ConfigLevel < 7)
64838032Speter				OperatorChars = macvalue('o', CurEnv);
64938032Speter			if (OperatorChars == NULL)
65038032Speter				OperatorChars = ".:@[]";
65138032Speter		}
65264562Sgshapiro		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
65364562Sgshapiro		       CurEnv);
65490792Sgshapiro		(void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf);
65538032Speter		for (p = obuf; *p != '\0'; p++)
65638032Speter		{
65738032Speter			if (TokTypeTab[*p & 0xff] == ATM)
65838032Speter				TokTypeTab[*p & 0xff] = OPR;
65964562Sgshapiro			if (TokTypeNoC[*p & 0xff] == ATM)
66064562Sgshapiro				TokTypeNoC[*p & 0xff] = OPR;
66138032Speter		}
66238032Speter	}
66338032Speter	if (toktab == NULL)
66438032Speter		toktab = TokTypeTab;
66538032Speter
66638032Speter	/* make sure error messages don't have garbage on them */
66738032Speter	errno = 0;
66838032Speter
66938032Speter	q = pvpbuf;
67090792Sgshapiro	bslashmode = false;
67190792Sgshapiro	route_syntax = false;
67238032Speter	cmntcnt = 0;
67338032Speter	anglecnt = 0;
67438032Speter	avp = av;
67538032Speter	state = ATM;
67638032Speter	c = NOCHAR;
67738032Speter	p = addr;
67838032Speter	CurEnv->e_to = p;
67938032Speter	if (tTd(22, 11))
68038032Speter	{
68190792Sgshapiro		sm_dprintf("prescan: ");
682132943Sgshapiro		xputs(sm_debug_file(), p);
68390792Sgshapiro		sm_dprintf("\n");
68438032Speter	}
68538032Speter
68638032Speter	do
68738032Speter	{
68838032Speter		/* read a token */
68938032Speter		tok = q;
69038032Speter		for (;;)
69138032Speter		{
69238032Speter			/* store away any old lookahead character */
69338032Speter			if (c != NOCHAR && !bslashmode)
69438032Speter			{
69538032Speter				/* see if there is room */
69638032Speter				if (q >= &pvpbuf[pvpbsize - 5])
69738032Speter				{
698112810Sgshapiro	addrtoolong:
69964562Sgshapiro					usrerr("553 5.1.1 Address too long");
70090792Sgshapiro					if (strlen(addr) > MAXNAME)
70138032Speter						addr[MAXNAME] = '\0';
70238032Speter	returnnull:
70338032Speter					if (delimptr != NULL)
704120169Snectar					{
705120169Snectar						if (p > addr)
706120256Sgshapiro							--p;
70738032Speter						*delimptr = p;
708120169Snectar					}
70938032Speter					CurEnv->e_to = saveto;
71064562Sgshapiro					return NULL;
71138032Speter				}
71238032Speter
71338032Speter				/* squirrel it away */
714112810Sgshapiro#if !ALLOW_255
715112810Sgshapiro				if ((char) c == (char) -1 && !tTd(82, 101))
716112810Sgshapiro					c &= 0x7f;
717112810Sgshapiro#endif /* !ALLOW_255 */
71838032Speter				*q++ = c;
71938032Speter			}
72038032Speter
72138032Speter			/* read a new input character */
722112810Sgshapiro			c = (*p++) & 0x00ff;
72338032Speter			if (c == '\0')
72438032Speter			{
72538032Speter				/* diagnose and patch up bad syntax */
726132943Sgshapiro				if (ignore)
727132943Sgshapiro					break;
728132943Sgshapiro				else if (state == QST)
72938032Speter				{
73090792Sgshapiro					usrerr("553 Unbalanced '\"'");
73138032Speter					c = '"';
73238032Speter				}
73338032Speter				else if (cmntcnt > 0)
73438032Speter				{
73590792Sgshapiro					usrerr("553 Unbalanced '('");
73638032Speter					c = ')';
73738032Speter				}
73838032Speter				else if (anglecnt > 0)
73938032Speter				{
74038032Speter					c = '>';
74190792Sgshapiro					usrerr("553 Unbalanced '<'");
74238032Speter				}
74338032Speter				else
74438032Speter					break;
74538032Speter
74638032Speter				p--;
74738032Speter			}
74838032Speter			else if (c == delim && cmntcnt <= 0 && state != QST)
74938032Speter			{
75038032Speter				if (anglecnt <= 0)
75138032Speter					break;
75238032Speter
75338032Speter				/* special case for better error management */
754132943Sgshapiro				if (delim == ',' && !route_syntax && !ignore)
75538032Speter				{
75690792Sgshapiro					usrerr("553 Unbalanced '<'");
75738032Speter					c = '>';
75838032Speter					p--;
75938032Speter				}
76038032Speter			}
76138032Speter
76238032Speter			if (tTd(22, 101))
76390792Sgshapiro				sm_dprintf("c=%c, s=%d; ", c, state);
76438032Speter
76538032Speter			/* chew up special characters */
76638032Speter			*q = '\0';
76738032Speter			if (bslashmode)
76838032Speter			{
76990792Sgshapiro				bslashmode = false;
77038032Speter
77138032Speter				/* kludge \! for naive users */
77238032Speter				if (cmntcnt > 0)
77338032Speter				{
77438032Speter					c = NOCHAR;
77538032Speter					continue;
77638032Speter				}
77738032Speter				else if (c != '!' || state == QST)
77838032Speter				{
779112810Sgshapiro					/* see if there is room */
780112810Sgshapiro					if (q >= &pvpbuf[pvpbsize - 5])
781112810Sgshapiro						goto addrtoolong;
78238032Speter					*q++ = '\\';
78338032Speter					continue;
78438032Speter				}
78538032Speter			}
78638032Speter
78738032Speter			if (c == '\\')
78838032Speter			{
78990792Sgshapiro				bslashmode = true;
79038032Speter			}
79138032Speter			else if (state == QST)
79238032Speter			{
79364562Sgshapiro				/* EMPTY */
79438032Speter				/* do nothing, just avoid next clauses */
79538032Speter			}
79664562Sgshapiro			else if (c == '(' && toktab['('] == SPC)
79738032Speter			{
79838032Speter				cmntcnt++;
79938032Speter				c = NOCHAR;
80038032Speter			}
80164562Sgshapiro			else if (c == ')' && toktab['('] == SPC)
80238032Speter			{
80338032Speter				if (cmntcnt <= 0)
80438032Speter				{
805132943Sgshapiro					if (!ignore)
806132943Sgshapiro					{
807132943Sgshapiro						usrerr("553 Unbalanced ')'");
808132943Sgshapiro						c = NOCHAR;
809132943Sgshapiro					}
81038032Speter				}
81138032Speter				else
81238032Speter					cmntcnt--;
81338032Speter			}
81438032Speter			else if (cmntcnt > 0)
81564562Sgshapiro			{
81638032Speter				c = NOCHAR;
81764562Sgshapiro			}
81838032Speter			else if (c == '<')
81938032Speter			{
82064562Sgshapiro				char *ptr = p;
82138032Speter
82238032Speter				anglecnt++;
82364562Sgshapiro				while (isascii(*ptr) && isspace(*ptr))
82464562Sgshapiro					ptr++;
82564562Sgshapiro				if (*ptr == '@')
82690792Sgshapiro					route_syntax = true;
82738032Speter			}
82838032Speter			else if (c == '>')
82938032Speter			{
83038032Speter				if (anglecnt <= 0)
83138032Speter				{
832132943Sgshapiro					if (!ignore)
833132943Sgshapiro					{
834132943Sgshapiro						usrerr("553 Unbalanced '>'");
835132943Sgshapiro						c = NOCHAR;
836132943Sgshapiro					}
83738032Speter				}
83838032Speter				else
83938032Speter					anglecnt--;
84090792Sgshapiro				route_syntax = false;
84138032Speter			}
84238032Speter			else if (delim == ' ' && isascii(c) && isspace(c))
84338032Speter				c = ' ';
84438032Speter
84538032Speter			if (c == NOCHAR)
84638032Speter				continue;
84738032Speter
84838032Speter			/* see if this is end of input */
84938032Speter			if (c == delim && anglecnt <= 0 && state != QST)
85038032Speter				break;
85138032Speter
85238032Speter			newstate = StateTab[state][toktab[c & 0xff]];
85338032Speter			if (tTd(22, 101))
85490792Sgshapiro				sm_dprintf("ns=%02o\n", newstate);
85538032Speter			state = newstate & TYPE;
85638032Speter			if (state == ILL)
85738032Speter			{
85838032Speter				if (isascii(c) && isprint(c))
85990792Sgshapiro					usrerr("553 Illegal character %c", c);
86038032Speter				else
86190792Sgshapiro					usrerr("553 Illegal character 0x%02x",
86290792Sgshapiro					       c & 0x0ff);
86338032Speter			}
86438032Speter			if (bitset(M, newstate))
86538032Speter				c = NOCHAR;
86638032Speter			if (bitset(B, newstate))
86738032Speter				break;
86838032Speter		}
86938032Speter
87038032Speter		/* new token */
87138032Speter		if (tok != q)
87238032Speter		{
873112810Sgshapiro			/* see if there is room */
874112810Sgshapiro			if (q >= &pvpbuf[pvpbsize - 5])
875112810Sgshapiro				goto addrtoolong;
87638032Speter			*q++ = '\0';
87738032Speter			if (tTd(22, 36))
87838032Speter			{
87990792Sgshapiro				sm_dprintf("tok=");
880132943Sgshapiro				xputs(sm_debug_file(), tok);
88190792Sgshapiro				sm_dprintf("\n");
88238032Speter			}
88338032Speter			if (avp >= &av[MAXATOM])
88438032Speter			{
88564562Sgshapiro				usrerr("553 5.1.0 prescan: too many tokens");
88638032Speter				goto returnnull;
88738032Speter			}
88838032Speter			if (q - tok > MAXNAME)
88938032Speter			{
89064562Sgshapiro				usrerr("553 5.1.0 prescan: token too long");
89138032Speter				goto returnnull;
89238032Speter			}
89338032Speter			*avp++ = tok;
89438032Speter		}
89538032Speter	} while (c != '\0' && (c != delim || anglecnt > 0));
89638032Speter	*avp = NULL;
89738032Speter	if (delimptr != NULL)
898120256Sgshapiro	{
899120256Sgshapiro		if (p > addr)
900120256Sgshapiro			p--;
90138032Speter		*delimptr = p;
902120256Sgshapiro	}
90338032Speter	if (tTd(22, 12))
90438032Speter	{
90590792Sgshapiro		sm_dprintf("prescan==>");
906132943Sgshapiro		printav(sm_debug_file(), av);
90738032Speter	}
90838032Speter	CurEnv->e_to = saveto;
90938032Speter	if (av[0] == NULL)
91038032Speter	{
91138032Speter		if (tTd(22, 1))
91290792Sgshapiro			sm_dprintf("prescan: null leading token\n");
91364562Sgshapiro		return NULL;
91438032Speter	}
91564562Sgshapiro	return av;
91638032Speter}
91790792Sgshapiro/*
91838032Speter**  REWRITE -- apply rewrite rules to token vector.
91938032Speter**
92038032Speter**	This routine is an ordered production system.  Each rewrite
92138032Speter**	rule has a LHS (called the pattern) and a RHS (called the
92238032Speter**	rewrite); 'rwr' points the the current rewrite rule.
92338032Speter**
92438032Speter**	For each rewrite rule, 'avp' points the address vector we
92538032Speter**	are trying to match against, and 'pvp' points to the pattern.
92638032Speter**	If pvp points to a special match value (MATCHZANY, MATCHANY,
92738032Speter**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
92838032Speter**	matched is saved away in the match vector (pointed to by 'mvp').
92938032Speter**
93038032Speter**	When a match between avp & pvp does not match, we try to
93138032Speter**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
93238032Speter**	we must also back out the match in mvp.  If we reach a
93338032Speter**	MATCHANY or MATCHZANY we just extend the match and start
93438032Speter**	over again.
93538032Speter**
93638032Speter**	When we finally match, we rewrite the address vector
93738032Speter**	and try over again.
93838032Speter**
93938032Speter**	Parameters:
94038032Speter**		pvp -- pointer to token vector.
94138032Speter**		ruleset -- the ruleset to use for rewriting.
94238032Speter**		reclevel -- recursion level (to catch loops).
94338032Speter**		e -- the current envelope.
94490792Sgshapiro**		maxatom -- maximum length of buffer (usually MAXATOM)
94538032Speter**
94638032Speter**	Returns:
94738032Speter**		A status code.  If EX_TEMPFAIL, higher level code should
94838032Speter**			attempt recovery.
94938032Speter**
95038032Speter**	Side Effects:
95138032Speter**		pvp is modified.
95238032Speter*/
95338032Speter
95438032Speterstruct match
95538032Speter{
95664562Sgshapiro	char	**match_first;		/* first token matched */
95764562Sgshapiro	char	**match_last;		/* last token matched */
95864562Sgshapiro	char	**match_pattern;	/* pointer to pattern */
95938032Speter};
96038032Speter
96138032Speterint
96290792Sgshapirorewrite(pvp, ruleset, reclevel, e, maxatom)
96338032Speter	char **pvp;
96438032Speter	int ruleset;
96538032Speter	int reclevel;
96638032Speter	register ENVELOPE *e;
96790792Sgshapiro	int maxatom;
96838032Speter{
96938032Speter	register char *ap;		/* address pointer */
97038032Speter	register char *rp;		/* rewrite pointer */
97164562Sgshapiro	register char *rulename;	/* ruleset name */
97264562Sgshapiro	register char *prefix;
97338032Speter	register char **avp;		/* address vector pointer */
97438032Speter	register char **rvp;		/* rewrite vector pointer */
97538032Speter	register struct match *mlp;	/* cur ptr into mlist */
97638032Speter	register struct rewrite *rwr;	/* pointer to current rewrite rule */
97738032Speter	int ruleno;			/* current rule number */
97838032Speter	int rstat = EX_OK;		/* return status */
97938032Speter	int loopcount;
98038032Speter	struct match mlist[MAXMATCH];	/* stores match on LHS */
98164562Sgshapiro	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */
98238032Speter	char buf[MAXLINE];
98364562Sgshapiro	char name[6];
98438032Speter
985120256Sgshapiro	/*
986120256Sgshapiro	**  mlp will not exceed mlist[] because readcf enforces
987120256Sgshapiro	**	the upper limit of entries when reading rulesets.
988120256Sgshapiro	*/
989120256Sgshapiro
99064562Sgshapiro	if (ruleset < 0 || ruleset >= MAXRWSETS)
99138032Speter	{
99264562Sgshapiro		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
99364562Sgshapiro		return EX_CONFIG;
99464562Sgshapiro	}
99564562Sgshapiro	rulename = RuleSetNames[ruleset];
99664562Sgshapiro	if (rulename == NULL)
99764562Sgshapiro	{
99890792Sgshapiro		(void) sm_snprintf(name, sizeof name, "%d", ruleset);
99964562Sgshapiro		rulename = name;
100064562Sgshapiro	}
100164562Sgshapiro	if (OpMode == MD_TEST)
100264562Sgshapiro		prefix = "";
100364562Sgshapiro	else
100464562Sgshapiro		prefix = "rewrite: ruleset ";
100564562Sgshapiro	if (OpMode == MD_TEST)
100664562Sgshapiro	{
100790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
100890792Sgshapiro				     "%s%-16.16s   input:", prefix, rulename);
1009132943Sgshapiro		printav(smioout, pvp);
101038032Speter	}
101164562Sgshapiro	else if (tTd(21, 1))
101238032Speter	{
101390792Sgshapiro		sm_dprintf("%s%-16.16s   input:", prefix, rulename);
1014132943Sgshapiro		printav(sm_debug_file(), pvp);
101538032Speter	}
101638032Speter	if (reclevel++ > MaxRuleRecursion)
101738032Speter	{
101864562Sgshapiro		syserr("rewrite: excessive recursion (max %d), ruleset %s",
101964562Sgshapiro			MaxRuleRecursion, rulename);
102038032Speter		return EX_CONFIG;
102138032Speter	}
102238032Speter	if (pvp == NULL)
102338032Speter		return EX_USAGE;
1024120256Sgshapiro	if (maxatom <= 0)
1025120256Sgshapiro		return EX_USAGE;
102638032Speter
102738032Speter	/*
102838032Speter	**  Run through the list of rewrite rules, applying
102938032Speter	**	any that match.
103038032Speter	*/
103138032Speter
103238032Speter	ruleno = 1;
103338032Speter	loopcount = 0;
103438032Speter	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
103538032Speter	{
103664562Sgshapiro		int status;
103738032Speter
103838032Speter		/* if already canonical, quit now */
103938032Speter		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
104038032Speter			break;
104138032Speter
104238032Speter		if (tTd(21, 12))
104338032Speter		{
104464562Sgshapiro			if (tTd(21, 15))
104590792Sgshapiro				sm_dprintf("-----trying rule (line %d):",
104664562Sgshapiro				       rwr->r_line);
104764562Sgshapiro			else
104890792Sgshapiro				sm_dprintf("-----trying rule:");
1049132943Sgshapiro			printav(sm_debug_file(), rwr->r_lhs);
105038032Speter		}
105138032Speter
105238032Speter		/* try to match on this rule */
105338032Speter		mlp = mlist;
105438032Speter		rvp = rwr->r_lhs;
105538032Speter		avp = pvp;
105638032Speter		if (++loopcount > 100)
105738032Speter		{
105864562Sgshapiro			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
105964562Sgshapiro				rulename, ruleno);
106038032Speter			if (tTd(21, 1))
106138032Speter			{
106290792Sgshapiro				sm_dprintf("workspace: ");
1063132943Sgshapiro				printav(sm_debug_file(), pvp);
106438032Speter			}
106538032Speter			break;
106638032Speter		}
106738032Speter
106838032Speter		while ((ap = *avp) != NULL || *rvp != NULL)
106938032Speter		{
107038032Speter			rp = *rvp;
107138032Speter			if (tTd(21, 35))
107238032Speter			{
107390792Sgshapiro				sm_dprintf("ADVANCE rp=");
1074132943Sgshapiro				xputs(sm_debug_file(), rp);
107590792Sgshapiro				sm_dprintf(", ap=");
1076132943Sgshapiro				xputs(sm_debug_file(), ap);
107790792Sgshapiro				sm_dprintf("\n");
107838032Speter			}
107938032Speter			if (rp == NULL)
108038032Speter			{
108138032Speter				/* end-of-pattern before end-of-address */
108238032Speter				goto backup;
108338032Speter			}
108438032Speter			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
108538032Speter			    (*rp & 0377) != MATCHZERO)
108638032Speter			{
108738032Speter				/* end-of-input with patterns left */
108838032Speter				goto backup;
108938032Speter			}
109038032Speter
109138032Speter			switch (*rp & 0377)
109238032Speter			{
109338032Speter			  case MATCHCLASS:
109438032Speter				/* match any phrase in a class */
109564562Sgshapiro				mlp->match_pattern = rvp;
109664562Sgshapiro				mlp->match_first = avp;
109738032Speter	extendclass:
109838032Speter				ap = *avp;
109938032Speter				if (ap == NULL)
110038032Speter					goto backup;
110164562Sgshapiro				mlp->match_last = avp++;
110264562Sgshapiro				cataddr(mlp->match_first, mlp->match_last,
110364562Sgshapiro					buf, sizeof buf, '\0');
110438032Speter				if (!wordinclass(buf, rp[1]))
110538032Speter				{
110638032Speter					if (tTd(21, 36))
110738032Speter					{
110890792Sgshapiro						sm_dprintf("EXTEND  rp=");
1109132943Sgshapiro						xputs(sm_debug_file(), rp);
111090792Sgshapiro						sm_dprintf(", ap=");
1111132943Sgshapiro						xputs(sm_debug_file(), ap);
111290792Sgshapiro						sm_dprintf("\n");
111338032Speter					}
111438032Speter					goto extendclass;
111538032Speter				}
111638032Speter				if (tTd(21, 36))
111790792Sgshapiro					sm_dprintf("CLMATCH\n");
111838032Speter				mlp++;
111938032Speter				break;
112038032Speter
112138032Speter			  case MATCHNCLASS:
112238032Speter				/* match any token not in a class */
112338032Speter				if (wordinclass(ap, rp[1]))
112438032Speter					goto backup;
112538032Speter
112664562Sgshapiro				/* FALLTHROUGH */
112738032Speter
112838032Speter			  case MATCHONE:
112938032Speter			  case MATCHANY:
113038032Speter				/* match exactly one token */
113164562Sgshapiro				mlp->match_pattern = rvp;
113264562Sgshapiro				mlp->match_first = avp;
113364562Sgshapiro				mlp->match_last = avp++;
113438032Speter				mlp++;
113538032Speter				break;
113638032Speter
113738032Speter			  case MATCHZANY:
113838032Speter				/* match zero or more tokens */
113964562Sgshapiro				mlp->match_pattern = rvp;
114064562Sgshapiro				mlp->match_first = avp;
114164562Sgshapiro				mlp->match_last = avp - 1;
114238032Speter				mlp++;
114338032Speter				break;
114438032Speter
114538032Speter			  case MATCHZERO:
114638032Speter				/* match zero tokens */
114738032Speter				break;
114838032Speter
114938032Speter			  case MACRODEXPAND:
115038032Speter				/*
115138032Speter				**  Match against run-time macro.
115238032Speter				**  This algorithm is broken for the
115338032Speter				**  general case (no recursive macros,
115438032Speter				**  improper tokenization) but should
115538032Speter				**  work for the usual cases.
115638032Speter				*/
115738032Speter
115838032Speter				ap = macvalue(rp[1], e);
115964562Sgshapiro				mlp->match_first = avp;
116038032Speter				if (tTd(21, 2))
116198841Sgshapiro					sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",
116238032Speter						macname(rp[1]),
116338032Speter						ap == NULL ? "(NULL)" : ap);
116438032Speter
116538032Speter				if (ap == NULL)
116638032Speter					break;
116738032Speter				while (*ap != '\0')
116838032Speter				{
116938032Speter					if (*avp == NULL ||
117090792Sgshapiro					    sm_strncasecmp(ap, *avp,
117190792Sgshapiro							   strlen(*avp)) != 0)
117238032Speter					{
117338032Speter						/* no match */
117464562Sgshapiro						avp = mlp->match_first;
117538032Speter						goto backup;
117638032Speter					}
117738032Speter					ap += strlen(*avp++);
117838032Speter				}
117938032Speter
118038032Speter				/* match */
118138032Speter				break;
118238032Speter
118338032Speter			  default:
118438032Speter				/* must have exact match */
118538032Speter				if (sm_strcasecmp(rp, ap))
118638032Speter					goto backup;
118738032Speter				avp++;
118838032Speter				break;
118938032Speter			}
119038032Speter
119138032Speter			/* successful match on this token */
119238032Speter			rvp++;
119338032Speter			continue;
119438032Speter
119538032Speter	  backup:
119638032Speter			/* match failed -- back up */
119738032Speter			while (--mlp >= mlist)
119838032Speter			{
119964562Sgshapiro				rvp = mlp->match_pattern;
120038032Speter				rp = *rvp;
120164562Sgshapiro				avp = mlp->match_last + 1;
120238032Speter				ap = *avp;
120338032Speter
120438032Speter				if (tTd(21, 36))
120538032Speter				{
120690792Sgshapiro					sm_dprintf("BACKUP  rp=");
1207132943Sgshapiro					xputs(sm_debug_file(), rp);
120890792Sgshapiro					sm_dprintf(", ap=");
1209132943Sgshapiro					xputs(sm_debug_file(), ap);
121090792Sgshapiro					sm_dprintf("\n");
121138032Speter				}
121238032Speter
121338032Speter				if (ap == NULL)
121438032Speter				{
121538032Speter					/* run off the end -- back up again */
121638032Speter					continue;
121738032Speter				}
121838032Speter				if ((*rp & 0377) == MATCHANY ||
121938032Speter				    (*rp & 0377) == MATCHZANY)
122038032Speter				{
122138032Speter					/* extend binding and continue */
122264562Sgshapiro					mlp->match_last = avp++;
122338032Speter					rvp++;
122438032Speter					mlp++;
122538032Speter					break;
122638032Speter				}
122738032Speter				if ((*rp & 0377) == MATCHCLASS)
122838032Speter				{
122938032Speter					/* extend binding and try again */
123064562Sgshapiro					mlp->match_last = avp;
123138032Speter					goto extendclass;
123238032Speter				}
123338032Speter			}
123438032Speter
123538032Speter			if (mlp < mlist)
123638032Speter			{
123738032Speter				/* total failure to match */
123838032Speter				break;
123938032Speter			}
124038032Speter		}
124138032Speter
124238032Speter		/*
124338032Speter		**  See if we successfully matched
124438032Speter		*/
124538032Speter
124638032Speter		if (mlp < mlist || *rvp != NULL)
124738032Speter		{
124838032Speter			if (tTd(21, 10))
124990792Sgshapiro				sm_dprintf("----- rule fails\n");
125038032Speter			rwr = rwr->r_next;
125138032Speter			ruleno++;
125238032Speter			loopcount = 0;
125338032Speter			continue;
125438032Speter		}
125538032Speter
125638032Speter		rvp = rwr->r_rhs;
125738032Speter		if (tTd(21, 12))
125838032Speter		{
125990792Sgshapiro			sm_dprintf("-----rule matches:");
1260132943Sgshapiro			printav(sm_debug_file(), rvp);
126138032Speter		}
126238032Speter
126338032Speter		rp = *rvp;
126464562Sgshapiro		if (rp != NULL)
126538032Speter		{
126664562Sgshapiro			if ((*rp & 0377) == CANONUSER)
126764562Sgshapiro			{
126864562Sgshapiro				rvp++;
126964562Sgshapiro				rwr = rwr->r_next;
127064562Sgshapiro				ruleno++;
127164562Sgshapiro				loopcount = 0;
127264562Sgshapiro			}
127364562Sgshapiro			else if ((*rp & 0377) == CANONHOST)
127464562Sgshapiro			{
127564562Sgshapiro				rvp++;
127664562Sgshapiro				rwr = NULL;
127764562Sgshapiro			}
127838032Speter		}
127938032Speter
128038032Speter		/* substitute */
128138032Speter		for (avp = npvp; *rvp != NULL; rvp++)
128238032Speter		{
128338032Speter			register struct match *m;
128438032Speter			register char **pp;
128538032Speter
128638032Speter			rp = *rvp;
128738032Speter			if ((*rp & 0377) == MATCHREPL)
128838032Speter			{
128938032Speter				/* substitute from LHS */
129038032Speter				m = &mlist[rp[1] - '1'];
129138032Speter				if (m < mlist || m >= mlp)
129238032Speter				{
129364562Sgshapiro					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
129464562Sgshapiro						rulename, rp[1]);
129538032Speter					return EX_CONFIG;
129638032Speter				}
129738032Speter				if (tTd(21, 15))
129838032Speter				{
129990792Sgshapiro					sm_dprintf("$%c:", rp[1]);
130064562Sgshapiro					pp = m->match_first;
130164562Sgshapiro					while (pp <= m->match_last)
130238032Speter					{
130390792Sgshapiro						sm_dprintf(" %p=\"", *pp);
130490792Sgshapiro						sm_dflush();
130590792Sgshapiro						sm_dprintf("%s\"", *pp++);
130638032Speter					}
130790792Sgshapiro					sm_dprintf("\n");
130838032Speter				}
130964562Sgshapiro				pp = m->match_first;
131064562Sgshapiro				while (pp <= m->match_last)
131138032Speter				{
131290792Sgshapiro					if (avp >= &npvp[maxatom])
1313120256Sgshapiro						goto toolong;
131438032Speter					*avp++ = *pp++;
131538032Speter				}
131638032Speter			}
131738032Speter			else
131838032Speter			{
131938032Speter				/* some sort of replacement */
132090792Sgshapiro				if (avp >= &npvp[maxatom])
132138032Speter				{
132238032Speter	toolong:
132364562Sgshapiro					syserr("554 5.3.0 rewrite: expansion too long");
132490792Sgshapiro					if (LogLevel > 9)
132590792Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
132690792Sgshapiro							"rewrite: expansion too long, ruleset=%s, ruleno=%d",
132790792Sgshapiro							rulename, ruleno);
132838032Speter					return EX_DATAERR;
132938032Speter				}
133038032Speter				if ((*rp & 0377) != MACRODEXPAND)
133138032Speter				{
133238032Speter					/* vanilla replacement */
133338032Speter					*avp++ = rp;
133438032Speter				}
133538032Speter				else
133638032Speter				{
133798841Sgshapiro					/* $&{x} replacement */
133838032Speter					char *mval = macvalue(rp[1], e);
133938032Speter					char **xpvp;
134038032Speter					int trsize = 0;
134138032Speter					static size_t pvpb1_size = 0;
134238032Speter					static char **pvpb1 = NULL;
134338032Speter					char pvpbuf[PSBUFSIZE];
134438032Speter
134538032Speter					if (tTd(21, 2))
134698841Sgshapiro						sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
134738032Speter							macname(rp[1]),
134838032Speter							mval == NULL ? "(NULL)" : mval);
134938032Speter					if (mval == NULL || *mval == '\0')
135038032Speter						continue;
135138032Speter
135238032Speter					/* save the remainder of the input */
135338032Speter					for (xpvp = pvp; *xpvp != NULL; xpvp++)
135438032Speter						trsize += sizeof *xpvp;
135564562Sgshapiro					if ((size_t) trsize > pvpb1_size)
135638032Speter					{
135738032Speter						if (pvpb1 != NULL)
135877349Sgshapiro							sm_free(pvpb1);
135990792Sgshapiro						pvpb1 = (char **)
136090792Sgshapiro							sm_pmalloc_x(trsize);
136138032Speter						pvpb1_size = trsize;
136238032Speter					}
136338032Speter
136464562Sgshapiro					memmove((char *) pvpb1,
136564562Sgshapiro						(char *) pvp,
136664562Sgshapiro						trsize);
136738032Speter
136838032Speter					/* scan the new replacement */
136938032Speter					xpvp = prescan(mval, '\0', pvpbuf,
137064562Sgshapiro						       sizeof pvpbuf, NULL,
1371132943Sgshapiro						       NULL, false);
137238032Speter					if (xpvp == NULL)
137338032Speter					{
137438032Speter						/* prescan pre-printed error */
137538032Speter						return EX_DATAERR;
137638032Speter					}
137738032Speter
137838032Speter					/* insert it into the output stream */
137938032Speter					while (*xpvp != NULL)
138038032Speter					{
138138032Speter						if (tTd(21, 19))
138290792Sgshapiro							sm_dprintf(" ... %s\n",
138364562Sgshapiro								*xpvp);
138490792Sgshapiro						*avp++ = sm_rpool_strdup_x(
138590792Sgshapiro							e->e_rpool, *xpvp);
138690792Sgshapiro						if (avp >= &npvp[maxatom])
138738032Speter							goto toolong;
138838032Speter						xpvp++;
138938032Speter					}
139038032Speter					if (tTd(21, 19))
139190792Sgshapiro						sm_dprintf(" ... DONE\n");
139238032Speter
139338032Speter					/* restore the old trailing input */
139464562Sgshapiro					memmove((char *) pvp,
139564562Sgshapiro						(char *) pvpb1,
139664562Sgshapiro						trsize);
139738032Speter				}
139838032Speter			}
139938032Speter		}
140038032Speter		*avp++ = NULL;
140138032Speter
140238032Speter		/*
140338032Speter		**  Check for any hostname/keyword lookups.
140438032Speter		*/
140538032Speter
140638032Speter		for (rvp = npvp; *rvp != NULL; rvp++)
140738032Speter		{
140838032Speter			char **hbrvp;
140938032Speter			char **xpvp;
141038032Speter			int trsize;
141138032Speter			char *replac;
141238032Speter			int endtoken;
141338032Speter			STAB *map;
141438032Speter			char *mapname;
141538032Speter			char **key_rvp;
141638032Speter			char **arg_rvp;
141738032Speter			char **default_rvp;
141864562Sgshapiro			char cbuf[MAXNAME + 1];
141938032Speter			char *pvpb1[MAXATOM + 1];
1420120256Sgshapiro			char *argvect[MAX_MAP_ARGS];
142138032Speter			char pvpbuf[PSBUFSIZE];
142238032Speter			char *nullpvp[1];
142338032Speter
142438032Speter			if ((**rvp & 0377) != HOSTBEGIN &&
142538032Speter			    (**rvp & 0377) != LOOKUPBEGIN)
142638032Speter				continue;
142738032Speter
142838032Speter			/*
142938032Speter			**  Got a hostname/keyword lookup.
143038032Speter			**
143138032Speter			**	This could be optimized fairly easily.
143238032Speter			*/
143338032Speter
143438032Speter			hbrvp = rvp;
143538032Speter			if ((**rvp & 0377) == HOSTBEGIN)
143638032Speter			{
143738032Speter				endtoken = HOSTEND;
143838032Speter				mapname = "host";
143938032Speter			}
144038032Speter			else
144138032Speter			{
144238032Speter				endtoken = LOOKUPEND;
144338032Speter				mapname = *++rvp;
1444120256Sgshapiro				if (mapname == NULL)
1445120256Sgshapiro					syserr("554 5.3.0 rewrite: missing mapname");
144638032Speter			}
144738032Speter			map = stab(mapname, ST_MAP, ST_FIND);
144838032Speter			if (map == NULL)
1449120256Sgshapiro				syserr("554 5.3.0 rewrite: map %s not found",
1450120256Sgshapiro					mapname);
145138032Speter
145238032Speter			/* extract the match part */
145338032Speter			key_rvp = ++rvp;
1454120256Sgshapiro			if (key_rvp == NULL)
1455120256Sgshapiro				syserr("554 5.3.0 rewrite: missing key for map %s",
1456120256Sgshapiro					mapname);
145738032Speter			default_rvp = NULL;
145838032Speter			arg_rvp = argvect;
145938032Speter			xpvp = NULL;
146038032Speter			replac = pvpbuf;
146138032Speter			while (*rvp != NULL && (**rvp & 0377) != endtoken)
146238032Speter			{
146338032Speter				int nodetype = **rvp & 0377;
146438032Speter
1465120256Sgshapiro				if (nodetype != CANONHOST &&
1466120256Sgshapiro				    nodetype != CANONUSER)
146738032Speter				{
146838032Speter					rvp++;
146938032Speter					continue;
147038032Speter				}
147138032Speter
147238032Speter				*rvp++ = NULL;
147338032Speter
147438032Speter				if (xpvp != NULL)
147538032Speter				{
147638032Speter					cataddr(xpvp, NULL, replac,
147738032Speter						&pvpbuf[sizeof pvpbuf] - replac,
147838032Speter						'\0');
1479120256Sgshapiro					if (arg_rvp <
1480120256Sgshapiro					    &argvect[MAX_MAP_ARGS - 1])
1481120256Sgshapiro						*++arg_rvp = replac;
148238032Speter					replac += strlen(replac) + 1;
148338032Speter					xpvp = NULL;
148438032Speter				}
148538032Speter				switch (nodetype)
148638032Speter				{
148738032Speter				  case CANONHOST:
148838032Speter					xpvp = rvp;
148938032Speter					break;
149038032Speter
149138032Speter				  case CANONUSER:
149238032Speter					default_rvp = rvp;
149338032Speter					break;
149438032Speter				}
149538032Speter			}
149638032Speter			if (*rvp != NULL)
149738032Speter				*rvp++ = NULL;
149838032Speter			if (xpvp != NULL)
149938032Speter			{
150038032Speter				cataddr(xpvp, NULL, replac,
150138032Speter					&pvpbuf[sizeof pvpbuf] - replac,
150238032Speter					'\0');
1503120256Sgshapiro				if (arg_rvp < &argvect[MAX_MAP_ARGS - 1])
1504120256Sgshapiro					*++arg_rvp = replac;
150538032Speter			}
1506120256Sgshapiro			if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1])
1507120256Sgshapiro				argvect[MAX_MAP_ARGS - 1] = NULL;
1508120256Sgshapiro			else
1509120256Sgshapiro				*++arg_rvp = NULL;
151038032Speter
151138032Speter			/* save the remainder of the input string */
151238032Speter			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
151364562Sgshapiro			memmove((char *) pvpb1, (char *) rvp, trsize);
151438032Speter
151538032Speter			/* look it up */
151664562Sgshapiro			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
151764562Sgshapiro				map == NULL ? '\0' : map->s_map.map_spacesub);
151864562Sgshapiro			argvect[0] = cbuf;
151964562Sgshapiro			replac = map_lookup(map, cbuf, argvect, &rstat, e);
152038032Speter
152138032Speter			/* if no replacement, use default */
152238032Speter			if (replac == NULL && default_rvp != NULL)
152338032Speter			{
152438032Speter				/* create the default */
152564562Sgshapiro				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
152664562Sgshapiro				replac = cbuf;
152738032Speter			}
152838032Speter
152938032Speter			if (replac == NULL)
153038032Speter			{
153138032Speter				xpvp = key_rvp;
153238032Speter			}
153338032Speter			else if (*replac == '\0')
153438032Speter			{
153538032Speter				/* null replacement */
153638032Speter				nullpvp[0] = NULL;
153738032Speter				xpvp = nullpvp;
153838032Speter			}
153938032Speter			else
154038032Speter			{
154138032Speter				/* scan the new replacement */
154238032Speter				xpvp = prescan(replac, '\0', pvpbuf,
1543132943Sgshapiro					       sizeof pvpbuf, NULL, NULL, false);
154438032Speter				if (xpvp == NULL)
154538032Speter				{
154638032Speter					/* prescan already printed error */
154738032Speter					return EX_DATAERR;
154838032Speter				}
154938032Speter			}
155038032Speter
155138032Speter			/* append it to the token list */
155238032Speter			for (avp = hbrvp; *xpvp != NULL; xpvp++)
155338032Speter			{
155490792Sgshapiro				*avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
155590792Sgshapiro				if (avp >= &npvp[maxatom])
155638032Speter					goto toolong;
155738032Speter			}
155838032Speter
155938032Speter			/* restore the old trailing information */
156038032Speter			rvp = avp - 1;
156138032Speter			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
156290792Sgshapiro				if (avp >= &npvp[maxatom])
156338032Speter					goto toolong;
156438032Speter		}
156538032Speter
156638032Speter		/*
156738032Speter		**  Check for subroutine calls.
156838032Speter		*/
156938032Speter
157064562Sgshapiro		status = callsubr(npvp, reclevel, e);
157164562Sgshapiro		if (rstat == EX_OK || status == EX_TEMPFAIL)
157264562Sgshapiro			rstat = status;
157338032Speter
157438032Speter		/* copy vector back into original space. */
157538032Speter		for (avp = npvp; *avp++ != NULL;)
157638032Speter			continue;
157764562Sgshapiro		memmove((char *) pvp, (char *) npvp,
157838032Speter		      (int) (avp - npvp) * sizeof *avp);
157964562Sgshapiro
158038032Speter		if (tTd(21, 4))
158138032Speter		{
158290792Sgshapiro			sm_dprintf("rewritten as:");
1583132943Sgshapiro			printav(sm_debug_file(), pvp);
158438032Speter		}
158538032Speter	}
158638032Speter
158764562Sgshapiro	if (OpMode == MD_TEST)
158838032Speter	{
158990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
159090792Sgshapiro				     "%s%-16.16s returns:", prefix, rulename);
1591132943Sgshapiro		printav(smioout, pvp);
159238032Speter	}
159364562Sgshapiro	else if (tTd(21, 1))
159464562Sgshapiro	{
159590792Sgshapiro		sm_dprintf("%s%-16.16s returns:", prefix, rulename);
1596132943Sgshapiro		printav(sm_debug_file(), pvp);
159764562Sgshapiro	}
159838032Speter	return rstat;
159938032Speter}
160090792Sgshapiro/*
160138032Speter**  CALLSUBR -- call subroutines in rewrite vector
160238032Speter**
160338032Speter**	Parameters:
160438032Speter**		pvp -- pointer to token vector.
160538032Speter**		reclevel -- the current recursion level.
160638032Speter**		e -- the current envelope.
160738032Speter**
160838032Speter**	Returns:
160938032Speter**		The status from the subroutine call.
161038032Speter**
161138032Speter**	Side Effects:
161238032Speter**		pvp is modified.
161338032Speter*/
161438032Speter
161564562Sgshapirostatic int
161638032Spetercallsubr(pvp, reclevel, e)
161738032Speter	char **pvp;
161838032Speter	int reclevel;
161938032Speter	ENVELOPE *e;
162038032Speter{
162164562Sgshapiro	char **avp;
162238032Speter	register int i;
162390792Sgshapiro	int subr, j;
162490792Sgshapiro	int nsubr;
162564562Sgshapiro	int status;
162638032Speter	int rstat = EX_OK;
162790792Sgshapiro#define MAX_SUBR	16
162890792Sgshapiro	int subrnumber[MAX_SUBR];
162990792Sgshapiro	int subrindex[MAX_SUBR];
163038032Speter
163190792Sgshapiro	nsubr = 0;
163290792Sgshapiro
163390792Sgshapiro	/*
163490792Sgshapiro	**  Look for subroutine calls in pvp, collect them into subr*[]
163590792Sgshapiro	**  We will perform the calls in the next loop, because we will
163690792Sgshapiro	**  call the "last" subroutine first to avoid recursive calls
163790792Sgshapiro	**  and too much copying.
163890792Sgshapiro	*/
163990792Sgshapiro
164090792Sgshapiro	for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
164138032Speter	{
164238032Speter		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
164338032Speter		{
164438032Speter			stripquotes(avp[1]);
164538032Speter			subr = strtorwset(avp[1], NULL, ST_FIND);
164638032Speter			if (subr < 0)
164738032Speter			{
164890792Sgshapiro				syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
164938032Speter				return EX_CONFIG;
165038032Speter			}
165138032Speter
165238032Speter			/*
165390792Sgshapiro			**  XXX instead of doing this we could optimize
165490792Sgshapiro			**  the rules after reading them: just remove
165590792Sgshapiro			**  calls to empty rulesets
165638032Speter			*/
165738032Speter
165890792Sgshapiro			/* subroutine is an empty ruleset?  don't call it */
165990792Sgshapiro			if (RewriteRules[subr] == NULL)
166090792Sgshapiro			{
166190792Sgshapiro				if (tTd(21, 3))
166290792Sgshapiro					sm_dprintf("-----skip subr %s (%d)\n",
166390792Sgshapiro						avp[1], subr);
166490792Sgshapiro				for (i = 2; avp[i] != NULL; i++)
166590792Sgshapiro					avp[i - 2] = avp[i];
166690792Sgshapiro				avp[i - 2] = NULL;
166738032Speter				continue;
166890792Sgshapiro			}
166990792Sgshapiro			if (++nsubr >= MAX_SUBR)
167038032Speter			{
167190792Sgshapiro				syserr("554 5.3.0 Too many subroutine calls (%d max)",
167290792Sgshapiro					MAX_SUBR);
167390792Sgshapiro				return EX_CONFIG;
167438032Speter			}
167590792Sgshapiro			subrnumber[nsubr] = subr;
167690792Sgshapiro			subrindex[nsubr] = j;
167790792Sgshapiro		}
167890792Sgshapiro	}
167938032Speter
168090792Sgshapiro	/*
168190792Sgshapiro	**  Perform the actual subroutines calls, "last" one first, i.e.,
168290792Sgshapiro	**  go from the right to the left through all calls,
168390792Sgshapiro	**  do the rewriting in place.
168490792Sgshapiro	*/
168538032Speter
168690792Sgshapiro	for (; nsubr > 0; nsubr--)
168790792Sgshapiro	{
168890792Sgshapiro		subr = subrnumber[nsubr];
168990792Sgshapiro		avp = pvp + subrindex[nsubr];
169038032Speter
169190792Sgshapiro		/* remove the subroutine call and name */
169290792Sgshapiro		for (i = 2; avp[i] != NULL; i++)
169390792Sgshapiro			avp[i - 2] = avp[i];
169490792Sgshapiro		avp[i - 2] = NULL;
169538032Speter
169690792Sgshapiro		/*
169790792Sgshapiro		**  Now we need to call the ruleset specified for
1698120256Sgshapiro		**  the subroutine. We can do this in place since
169990792Sgshapiro		**  we call the "last" subroutine first.
170090792Sgshapiro		*/
170190792Sgshapiro
170290792Sgshapiro		status = rewrite(avp, subr, reclevel, e,
170390792Sgshapiro				MAXATOM - subrindex[nsubr]);
170490792Sgshapiro		if (status != EX_OK && status != EX_TEMPFAIL)
170590792Sgshapiro			return status;
170690792Sgshapiro		if (rstat == EX_OK || status == EX_TEMPFAIL)
170790792Sgshapiro			rstat = status;
170838032Speter	}
170938032Speter	return rstat;
171038032Speter}
171190792Sgshapiro/*
171238032Speter**  MAP_LOOKUP -- do lookup in map
171338032Speter**
171438032Speter**	Parameters:
171590792Sgshapiro**		smap -- the map to use for the lookup.
171638032Speter**		key -- the key to look up.
171738032Speter**		argvect -- arguments to pass to the map lookup.
171838032Speter**		pstat -- a pointer to an integer in which to store the
171938032Speter**			status from the lookup.
172038032Speter**		e -- the current envelope.
172138032Speter**
172238032Speter**	Returns:
172338032Speter**		The result of the lookup.
172438032Speter**		NULL -- if there was no data for the given key.
172538032Speter*/
172638032Speter
172764562Sgshapirostatic char *
172864562Sgshapiromap_lookup(smap, key, argvect, pstat, e)
172964562Sgshapiro	STAB *smap;
173038032Speter	char key[];
173138032Speter	char **argvect;
173238032Speter	int *pstat;
173338032Speter	ENVELOPE *e;
173438032Speter{
173564562Sgshapiro	auto int status = EX_OK;
173664562Sgshapiro	MAP *map;
173738032Speter	char *replac;
173838032Speter
173964562Sgshapiro	if (smap == NULL)
174064562Sgshapiro		return NULL;
174164562Sgshapiro
174264562Sgshapiro	map = &smap->s_map;
174364562Sgshapiro	DYNOPENMAP(map);
174464562Sgshapiro
174564562Sgshapiro	if (e->e_sendmode == SM_DEFER &&
174664562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
174738032Speter	{
174838032Speter		/* don't do any map lookups */
174938032Speter		if (tTd(60, 1))
175090792Sgshapiro			sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
175164562Sgshapiro				smap->s_name, key);
175238032Speter		*pstat = EX_TEMPFAIL;
175338032Speter		return NULL;
175438032Speter	}
175538032Speter
175664562Sgshapiro	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
175738032Speter		stripquotes(key);
175838032Speter
175938032Speter	if (tTd(60, 1))
176042575Speter	{
176190792Sgshapiro		sm_dprintf("map_lookup(%s, %s", smap->s_name, key);
176242575Speter		if (tTd(60, 5))
176342575Speter		{
176442575Speter			int i;
176542575Speter
176642575Speter			for (i = 0; argvect[i] != NULL; i++)
176790792Sgshapiro				sm_dprintf(", %%%d=%s", i, argvect[i]);
176842575Speter		}
176990792Sgshapiro		sm_dprintf(") => ");
177042575Speter	}
177164562Sgshapiro	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
177238032Speter	if (tTd(60, 1))
177390792Sgshapiro		sm_dprintf("%s (%d)\n",
177438032Speter			replac != NULL ? replac : "NOT FOUND",
177564562Sgshapiro			status);
177638032Speter
177764562Sgshapiro	/* should recover if status == EX_TEMPFAIL */
177864562Sgshapiro	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
177938032Speter	{
178038032Speter		*pstat = EX_TEMPFAIL;
178138032Speter		if (tTd(60, 1))
178290792Sgshapiro			sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
178364562Sgshapiro				smap->s_name, key, errno);
178438032Speter		if (e->e_message == NULL)
178538032Speter		{
178638032Speter			char mbuf[320];
178738032Speter
178890792Sgshapiro			(void) sm_snprintf(mbuf, sizeof mbuf,
178938032Speter				"%.80s map: lookup (%s): deferred",
179064562Sgshapiro				smap->s_name,
179138032Speter				shortenstring(key, MAXSHORTSTR));
179290792Sgshapiro			e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
179338032Speter		}
179438032Speter	}
179564562Sgshapiro	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
179638032Speter	{
179764562Sgshapiro		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
179838032Speter		static char *rwbuf = NULL;
179938032Speter		static size_t rwbuflen = 0;
180038032Speter
180138032Speter		if (i > rwbuflen)
180238032Speter		{
180338032Speter			if (rwbuf != NULL)
180477349Sgshapiro				sm_free(rwbuf);
180538032Speter			rwbuflen = i;
180690792Sgshapiro			rwbuf = (char *) sm_pmalloc_x(rwbuflen);
180738032Speter		}
180890792Sgshapiro		(void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
180938032Speter		if (tTd(60, 4))
181090792Sgshapiro			sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
181138032Speter				rwbuf);
181238032Speter		return rwbuf;
181338032Speter	}
181438032Speter	return replac;
181538032Speter}
181690792Sgshapiro/*
181764562Sgshapiro**  INITERRMAILERS -- initialize error and discard mailers
181864562Sgshapiro**
181964562Sgshapiro**	Parameters:
182064562Sgshapiro**		none.
182164562Sgshapiro**
182264562Sgshapiro**	Returns:
182364562Sgshapiro**		none.
182464562Sgshapiro**
182564562Sgshapiro**	Side Effects:
182664562Sgshapiro**		initializes error and discard mailers.
182764562Sgshapiro*/
182864562Sgshapiro
182964562Sgshapirostatic MAILER discardmailer;
183064562Sgshapirostatic MAILER errormailer;
183164562Sgshapirostatic char *discardargv[] = { "DISCARD", NULL };
183264562Sgshapirostatic char *errorargv[] = { "ERROR", NULL };
183364562Sgshapiro
183464562Sgshapirovoid
183564562Sgshapiroiniterrmailers()
183664562Sgshapiro{
183764562Sgshapiro	if (discardmailer.m_name == NULL)
183864562Sgshapiro	{
183964562Sgshapiro		/* initialize the discard mailer */
184064562Sgshapiro		discardmailer.m_name = "*discard*";
184164562Sgshapiro		discardmailer.m_mailer = "DISCARD";
184264562Sgshapiro		discardmailer.m_argv = discardargv;
184364562Sgshapiro	}
184464562Sgshapiro	if (errormailer.m_name == NULL)
184564562Sgshapiro	{
184664562Sgshapiro		/* initialize the bogus mailer */
184764562Sgshapiro		errormailer.m_name = "*error*";
184864562Sgshapiro		errormailer.m_mailer = "ERROR";
184964562Sgshapiro		errormailer.m_argv = errorargv;
185064562Sgshapiro	}
185164562Sgshapiro}
185290792Sgshapiro/*
185338032Speter**  BUILDADDR -- build address from token vector.
185438032Speter**
185538032Speter**	Parameters:
185638032Speter**		tv -- token vector.
185738032Speter**		a -- pointer to address descriptor to fill.
185838032Speter**			If NULL, one will be allocated.
185938032Speter**		flags -- info regarding whether this is a sender or
186038032Speter**			a recipient.
186138032Speter**		e -- the current envelope.
186238032Speter**
186338032Speter**	Returns:
186438032Speter**		NULL if there was an error.
186538032Speter**		'a' otherwise.
186638032Speter**
186738032Speter**	Side Effects:
186838032Speter**		fills in 'a'
186938032Speter*/
187038032Speter
187164562Sgshapirostatic struct errcodes
187238032Speter{
187338032Speter	char	*ec_name;		/* name of error code */
187438032Speter	int	ec_code;		/* numeric code */
187538032Speter} ErrorCodes[] =
187638032Speter{
187738032Speter	{ "usage",		EX_USAGE	},
187838032Speter	{ "nouser",		EX_NOUSER	},
187938032Speter	{ "nohost",		EX_NOHOST	},
188038032Speter	{ "unavailable",	EX_UNAVAILABLE	},
188138032Speter	{ "software",		EX_SOFTWARE	},
188238032Speter	{ "tempfail",		EX_TEMPFAIL	},
188338032Speter	{ "protocol",		EX_PROTOCOL	},
188438032Speter	{ "config",		EX_CONFIG	},
188538032Speter	{ NULL,			EX_UNAVAILABLE	}
188638032Speter};
188738032Speter
188864562Sgshapirostatic ADDRESS *
188938032Speterbuildaddr(tv, a, flags, e)
189038032Speter	register char **tv;
189138032Speter	register ADDRESS *a;
189238032Speter	int flags;
189338032Speter	register ENVELOPE *e;
189438032Speter{
189594334Sgshapiro	bool tempfail = false;
1896120256Sgshapiro	int maxatom;
189738032Speter	struct mailer **mp;
189838032Speter	register struct mailer *m;
189938032Speter	register char *p;
190038032Speter	char *mname;
190138032Speter	char **hostp;
190238032Speter	char hbuf[MAXNAME + 1];
190342575Speter	static char ubuf[MAXNAME + 2];
190438032Speter
190538032Speter	if (tTd(24, 5))
190638032Speter	{
190790792Sgshapiro		sm_dprintf("buildaddr, flags=%x, tv=", flags);
1908132943Sgshapiro		printav(sm_debug_file(), tv);
190938032Speter	}
191038032Speter
1911120256Sgshapiro	maxatom = MAXATOM;
191238032Speter	if (a == NULL)
191390792Sgshapiro		a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
191464562Sgshapiro	memset((char *) a, '\0', sizeof *a);
191564562Sgshapiro	hbuf[0] = '\0';
191638032Speter
191738032Speter	/* set up default error return flags */
191838032Speter	a->q_flags |= DefaultNotify;
191938032Speter
192038032Speter	/* figure out what net/mailer to use */
192138032Speter	if (*tv == NULL || (**tv & 0377) != CANONNET)
192238032Speter	{
192364562Sgshapiro		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
192438032Speterbadaddr:
192594334Sgshapiro		/*
192694334Sgshapiro		**  ExitStat may have been set by an earlier map open
192794334Sgshapiro		**  failure (to a permanent error (EX_OSERR) in syserr())
192894334Sgshapiro		**  so we also need to check if this particular $#error
192994334Sgshapiro		**  return wanted a 4XX failure.
193094334Sgshapiro		**
193194334Sgshapiro		**  XXX the real fix is probably to set ExitStat correctly,
193294334Sgshapiro		**  i.e., to EX_TEMPFAIL if the map open is just a temporary
193394334Sgshapiro		**  error.
193494334Sgshapiro		*/
193594334Sgshapiro
193694334Sgshapiro		if (ExitStat == EX_TEMPFAIL || tempfail)
193764562Sgshapiro			a->q_state = QS_QUEUEUP;
193864562Sgshapiro		else
193938032Speter		{
194064562Sgshapiro			a->q_state = QS_BADADDR;
194164562Sgshapiro			a->q_mailer = &errormailer;
194238032Speter		}
194338032Speter		return a;
194438032Speter	}
194538032Speter	mname = *++tv;
1946120256Sgshapiro	--maxatom;
194738032Speter
194838032Speter	/* extract host and user portions */
194938032Speter	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1950120256Sgshapiro	{
195138032Speter		hostp = ++tv;
1952120256Sgshapiro		--maxatom;
1953120256Sgshapiro	}
195438032Speter	else
195538032Speter		hostp = NULL;
1956120256Sgshapiro	--maxatom;
195738032Speter	while (*tv != NULL && (**tv & 0377) != CANONUSER)
1958120256Sgshapiro	{
195938032Speter		tv++;
1960120256Sgshapiro		--maxatom;
1961120256Sgshapiro	}
196238032Speter	if (*tv == NULL)
196338032Speter	{
196464562Sgshapiro		syserr("554 5.3.5 buildaddr: no user");
196538032Speter		goto badaddr;
196638032Speter	}
196738032Speter	if (tv == hostp)
196838032Speter		hostp = NULL;
196938032Speter	else if (hostp != NULL)
197038032Speter		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
197138032Speter	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1972120256Sgshapiro	--maxatom;
197338032Speter
197438032Speter	/* save away the host name */
197590792Sgshapiro	if (sm_strcasecmp(mname, "error") == 0)
197638032Speter	{
197764562Sgshapiro		/* Set up triplet for use by -bv */
197864562Sgshapiro		a->q_mailer = &errormailer;
197990792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
198090792Sgshapiro		/* XXX wrong place? */
198164562Sgshapiro
198238032Speter		if (hostp != NULL)
198338032Speter		{
198438032Speter			register struct errcodes *ep;
198538032Speter
198690792Sgshapiro			a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
198738032Speter			if (strchr(hbuf, '.') != NULL)
198838032Speter			{
198990792Sgshapiro				a->q_status = sm_rpool_strdup_x(e->e_rpool,
199090792Sgshapiro								hbuf);
199138032Speter				setstat(dsntoexitstat(hbuf));
199238032Speter			}
199338032Speter			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
199438032Speter			{
199538032Speter				setstat(atoi(hbuf));
199638032Speter			}
199738032Speter			else
199838032Speter			{
199938032Speter				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
200090792Sgshapiro					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
200138032Speter						break;
200238032Speter				setstat(ep->ec_code);
200338032Speter			}
200438032Speter		}
200538032Speter		else
200664562Sgshapiro		{
200764562Sgshapiro			a->q_host = NULL;
200838032Speter			setstat(EX_UNAVAILABLE);
200964562Sgshapiro		}
201038032Speter		stripquotes(ubuf);
201164562Sgshapiro		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
201238032Speter		{
201364562Sgshapiro			char fmt[16];
201464562Sgshapiro			int off;
201538032Speter
201664562Sgshapiro			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
201738032Speter			{
201864562Sgshapiro				ubuf[off + 4] = '\0';
201964562Sgshapiro				off += 5;
202038032Speter			}
202164562Sgshapiro			else
202264562Sgshapiro			{
202364562Sgshapiro				off = 4;
202464562Sgshapiro				ubuf[3] = '\0';
202564562Sgshapiro			}
202690792Sgshapiro			(void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s");
202764562Sgshapiro			if (off > 4)
202864562Sgshapiro				usrerr(fmt, ubuf + off);
202964562Sgshapiro			else if (isenhsc(hbuf, '\0') > 0)
203064562Sgshapiro				usrerrenh(hbuf, fmt, ubuf + off);
203164562Sgshapiro			else
203264562Sgshapiro				usrerr(fmt, ubuf + off);
203364562Sgshapiro			/* XXX ubuf[off - 1] = ' '; */
203494334Sgshapiro			if (ubuf[0] == '4')
203594334Sgshapiro				tempfail = true;
203638032Speter		}
203738032Speter		else
203838032Speter		{
203964562Sgshapiro			usrerr("553 5.3.0 %s", ubuf);
204038032Speter		}
204138032Speter		goto badaddr;
204238032Speter	}
204338032Speter
204438032Speter	for (mp = Mailer; (m = *mp++) != NULL; )
204538032Speter	{
204690792Sgshapiro		if (sm_strcasecmp(m->m_name, mname) == 0)
204738032Speter			break;
204838032Speter	}
204938032Speter	if (m == NULL)
205038032Speter	{
205164562Sgshapiro		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
205238032Speter		goto badaddr;
205338032Speter	}
205438032Speter	a->q_mailer = m;
205538032Speter
205638032Speter	/* figure out what host (if any) */
205738032Speter	if (hostp == NULL)
205838032Speter	{
205938032Speter		if (!bitnset(M_LOCALMAILER, m->m_flags))
206038032Speter		{
206164562Sgshapiro			syserr("554 5.3.5 buildaddr: no host");
206238032Speter			goto badaddr;
206338032Speter		}
206438032Speter		a->q_host = NULL;
206538032Speter	}
206638032Speter	else
206790792Sgshapiro		a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
206838032Speter
206938032Speter	/* figure out the user */
207038032Speter	p = ubuf;
207138032Speter	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
207238032Speter	{
207338032Speter		p++;
207438032Speter		tv++;
2075120256Sgshapiro		--maxatom;
207638032Speter		a->q_flags |= QNOTREMOTE;
207738032Speter	}
207838032Speter
207938032Speter	/* do special mapping for local mailer */
208038032Speter	if (*p == '"')
208138032Speter		p++;
208238032Speter	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
208338032Speter		a->q_mailer = m = ProgMailer;
208438032Speter	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
208538032Speter		a->q_mailer = m = FileMailer;
208638032Speter	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
208738032Speter	{
208838032Speter		/* may be :include: */
208938032Speter		stripquotes(ubuf);
209090792Sgshapiro		if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
209138032Speter		{
209238032Speter			/* if :include:, don't need further rewriting */
209338032Speter			a->q_mailer = m = InclMailer;
209490792Sgshapiro			a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
209538032Speter			return a;
209638032Speter		}
209738032Speter	}
209838032Speter
209938032Speter	/* rewrite according recipient mailer rewriting rules */
210090792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
210164562Sgshapiro
210290792Sgshapiro	if (ConfigLevel >= 10 ||
210364562Sgshapiro	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
210438032Speter	{
210538032Speter		/* sender addresses done later */
2106120256Sgshapiro		(void) rewrite(tv, 2, 0, e, maxatom);
210738032Speter		if (m->m_re_rwset > 0)
2108120256Sgshapiro		       (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom);
210938032Speter	}
2110120256Sgshapiro	(void) rewrite(tv, 4, 0, e, maxatom);
211138032Speter
211238032Speter	/* save the result for the command line/RCPT argument */
211338032Speter	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
211490792Sgshapiro	a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
211538032Speter
211638032Speter	/*
211738032Speter	**  Do mapping to lower case as requested by mailer
211838032Speter	*/
211938032Speter
212038032Speter	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
212138032Speter		makelower(a->q_host);
212238032Speter	if (!bitnset(M_USR_UPPER, m->m_flags))
212338032Speter		makelower(a->q_user);
212438032Speter
212538032Speter	if (tTd(24, 6))
212638032Speter	{
212790792Sgshapiro		sm_dprintf("buildaddr => ");
2128132943Sgshapiro		printaddr(sm_debug_file(), a, false);
212938032Speter	}
213038032Speter	return a;
213138032Speter}
2132110560Sgshapiro
213390792Sgshapiro/*
213438032Speter**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
213538032Speter**
213638032Speter**	Parameters:
213738032Speter**		pvp -- parameter vector to rebuild.
213838032Speter**		evp -- last parameter to include.  Can be NULL to
213938032Speter**			use entire pvp.
214038032Speter**		buf -- buffer to build the string into.
214138032Speter**		sz -- size of buf.
214290792Sgshapiro**		spacesub -- the space separator character; if '\0',
214338032Speter**			use SpaceSub.
214438032Speter**
214538032Speter**	Returns:
214638032Speter**		none.
214738032Speter**
214838032Speter**	Side Effects:
214938032Speter**		Destroys buf.
215038032Speter*/
215138032Speter
215238032Spetervoid
215338032Spetercataddr(pvp, evp, buf, sz, spacesub)
215438032Speter	char **pvp;
215538032Speter	char **evp;
215638032Speter	char *buf;
215738032Speter	register int sz;
215838032Speter	int spacesub;
215938032Speter{
216090792Sgshapiro	bool oatomtok = false;
216190792Sgshapiro	bool natomtok = false;
216238032Speter	register int i;
216338032Speter	register char *p;
216438032Speter
216564562Sgshapiro	if (sz <= 0)
216664562Sgshapiro		return;
216764562Sgshapiro
216838032Speter	if (spacesub == '\0')
216938032Speter		spacesub = SpaceSub;
217038032Speter
217138032Speter	if (pvp == NULL)
217238032Speter	{
217364562Sgshapiro		*buf = '\0';
217438032Speter		return;
217538032Speter	}
217638032Speter	p = buf;
217738032Speter	sz -= 2;
217890792Sgshapiro	while (*pvp != NULL && sz > 0)
217938032Speter	{
218038032Speter		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
218138032Speter		if (oatomtok && natomtok)
218264562Sgshapiro		{
218338032Speter			*p++ = spacesub;
218490792Sgshapiro			if (--sz <= 0)
218590792Sgshapiro				break;
218664562Sgshapiro		}
2187132943Sgshapiro		i = sm_strlcpy(p, *pvp, sz);
2188132943Sgshapiro		sz -= i;
2189132943Sgshapiro		if (sz <= 0)
219090792Sgshapiro			break;
219138032Speter		oatomtok = natomtok;
219238032Speter		p += i;
219338032Speter		if (pvp++ == evp)
219438032Speter			break;
219538032Speter	}
2196132943Sgshapiro
2197132943Sgshapiro	/* Don't silently truncate long strings */
2198132943Sgshapiro	if (sz <= 0)
219994334Sgshapiro		syserr("cataddr: string too long");
220038032Speter	*p = '\0';
220138032Speter}
220290792Sgshapiro/*
220338032Speter**  SAMEADDR -- Determine if two addresses are the same
220438032Speter**
220538032Speter**	This is not just a straight comparison -- if the mailer doesn't
220638032Speter**	care about the host we just ignore it, etc.
220738032Speter**
220838032Speter**	Parameters:
220938032Speter**		a, b -- pointers to the internal forms to compare.
221038032Speter**
221138032Speter**	Returns:
221290792Sgshapiro**		true -- they represent the same mailbox.
221390792Sgshapiro**		false -- they don't.
221438032Speter**
221538032Speter**	Side Effects:
221638032Speter**		none.
221738032Speter*/
221838032Speter
221938032Speterbool
222038032Spetersameaddr(a, b)
222138032Speter	register ADDRESS *a;
222238032Speter	register ADDRESS *b;
222338032Speter{
222438032Speter	register ADDRESS *ca, *cb;
222538032Speter
222638032Speter	/* if they don't have the same mailer, forget it */
222738032Speter	if (a->q_mailer != b->q_mailer)
222890792Sgshapiro		return false;
222938032Speter
223038032Speter	/* if the user isn't the same, we can drop out */
223138032Speter	if (strcmp(a->q_user, b->q_user) != 0)
223290792Sgshapiro		return false;
223338032Speter
223438032Speter	/* if we have good uids for both but they differ, these are different */
223538032Speter	if (a->q_mailer == ProgMailer)
223638032Speter	{
223738032Speter		ca = getctladdr(a);
223838032Speter		cb = getctladdr(b);
223938032Speter		if (ca != NULL && cb != NULL &&
224038032Speter		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
224138032Speter		    ca->q_uid != cb->q_uid)
224290792Sgshapiro			return false;
224338032Speter	}
224438032Speter
224538032Speter	/* otherwise compare hosts (but be careful for NULL ptrs) */
224638032Speter	if (a->q_host == b->q_host)
224738032Speter	{
224838032Speter		/* probably both null pointers */
224990792Sgshapiro		return true;
225038032Speter	}
225138032Speter	if (a->q_host == NULL || b->q_host == NULL)
225238032Speter	{
225338032Speter		/* only one is a null pointer */
225490792Sgshapiro		return false;
225538032Speter	}
225638032Speter	if (strcmp(a->q_host, b->q_host) != 0)
225790792Sgshapiro		return false;
225838032Speter
225990792Sgshapiro	return true;
226038032Speter}
226190792Sgshapiro/*
226238032Speter**  PRINTADDR -- print address (for debugging)
226338032Speter**
226438032Speter**	Parameters:
226538032Speter**		a -- the address to print
226638032Speter**		follow -- follow the q_next chain.
226738032Speter**
226838032Speter**	Returns:
226938032Speter**		none.
227038032Speter**
227138032Speter**	Side Effects:
227238032Speter**		none.
227338032Speter*/
227438032Speter
227538032Speterstruct qflags
227638032Speter{
227790792Sgshapiro	char		*qf_name;
227890792Sgshapiro	unsigned long	qf_bit;
227938032Speter};
228038032Speter
228164562Sgshapirostatic struct qflags	AddressFlags[] =
228238032Speter{
228338032Speter	{ "QGOODUID",		QGOODUID	},
228438032Speter	{ "QPRIMARY",		QPRIMARY	},
228538032Speter	{ "QNOTREMOTE",		QNOTREMOTE	},
228638032Speter	{ "QSELFREF",		QSELFREF	},
228738032Speter	{ "QBOGUSSHELL",	QBOGUSSHELL	},
228838032Speter	{ "QUNSAFEADDR",	QUNSAFEADDR	},
228938032Speter	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
229038032Speter	{ "QPINGONFAILURE",	QPINGONFAILURE	},
229138032Speter	{ "QPINGONDELAY",	QPINGONDELAY	},
229238032Speter	{ "QHASNOTIFY",		QHASNOTIFY	},
229338032Speter	{ "QRELAYED",		QRELAYED	},
229438032Speter	{ "QEXPANDED",		QEXPANDED	},
229538032Speter	{ "QDELIVERED",		QDELIVERED	},
229638032Speter	{ "QDELAYED",		QDELAYED	},
229738032Speter	{ "QTHISPASS",		QTHISPASS	},
229838032Speter	{ "QRCPTOK",		QRCPTOK		},
229971345Sgshapiro	{ NULL,			0		}
230038032Speter};
230138032Speter
230238032Spetervoid
2303132943Sgshapiroprintaddr(fp, a, follow)
2304132943Sgshapiro	SM_FILE_T *fp;
230538032Speter	register ADDRESS *a;
230638032Speter	bool follow;
230738032Speter{
230838032Speter	register MAILER *m;
230938032Speter	MAILER pseudomailer;
231038032Speter	register struct qflags *qfp;
231138032Speter	bool firstone;
231238032Speter
231338032Speter	if (a == NULL)
231438032Speter	{
2315132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n");
231638032Speter		return;
231738032Speter	}
231838032Speter
231938032Speter	while (a != NULL)
232038032Speter	{
2321132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a);
2322132943Sgshapiro		(void) sm_io_flush(fp, SM_TIME_DEFAULT);
232338032Speter
232438032Speter		/* find the mailer -- carefully */
232538032Speter		m = a->q_mailer;
232638032Speter		if (m == NULL)
232738032Speter		{
232838032Speter			m = &pseudomailer;
232938032Speter			m->m_mno = -1;
233038032Speter			m->m_name = "NULL";
233138032Speter		}
233238032Speter
2333132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
233490792Sgshapiro				     "%s:\n\tmailer %d (%s), host `%s'\n",
233590792Sgshapiro				     a->q_paddr == NULL ? "<null>" : a->q_paddr,
233690792Sgshapiro				     m->m_mno, m->m_name,
233790792Sgshapiro				     a->q_host == NULL ? "<null>" : a->q_host);
2338132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
233990792Sgshapiro				     "\tuser `%s', ruser `%s'\n",
234090792Sgshapiro				     a->q_user,
234190792Sgshapiro				     a->q_ruser == NULL ? "<null>" : a->q_ruser);
2342132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate=");
234364562Sgshapiro		switch (a->q_state)
234464562Sgshapiro		{
234564562Sgshapiro		  case QS_OK:
2346132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK");
234764562Sgshapiro			break;
234864562Sgshapiro
234964562Sgshapiro		  case QS_DONTSEND:
2350132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
235190792Sgshapiro					     "DONTSEND");
235264562Sgshapiro			break;
235364562Sgshapiro
235464562Sgshapiro		  case QS_BADADDR:
2355132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
235690792Sgshapiro					     "BADADDR");
235764562Sgshapiro			break;
235864562Sgshapiro
235964562Sgshapiro		  case QS_QUEUEUP:
2360132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
236190792Sgshapiro					     "QUEUEUP");
236264562Sgshapiro			break;
236364562Sgshapiro
236490792Sgshapiro		  case QS_RETRY:
2365132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY");
236690792Sgshapiro			break;
236790792Sgshapiro
236864562Sgshapiro		  case QS_SENT:
2369132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT");
237064562Sgshapiro			break;
237164562Sgshapiro
237264562Sgshapiro		  case QS_VERIFIED:
2373132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
237490792Sgshapiro					     "VERIFIED");
237564562Sgshapiro			break;
237664562Sgshapiro
237764562Sgshapiro		  case QS_EXPANDED:
2378132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
237990792Sgshapiro					     "EXPANDED");
238064562Sgshapiro			break;
238164562Sgshapiro
238264562Sgshapiro		  case QS_SENDER:
2383132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
238490792Sgshapiro					     "SENDER");
238564562Sgshapiro			break;
238664562Sgshapiro
238764562Sgshapiro		  case QS_CLONED:
2388132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
238990792Sgshapiro					     "CLONED");
239064562Sgshapiro			break;
239164562Sgshapiro
239264562Sgshapiro		  case QS_DISCARDED:
2393132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
239490792Sgshapiro					     "DISCARDED");
239564562Sgshapiro			break;
239664562Sgshapiro
239764562Sgshapiro		  case QS_REPLACED:
2398132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
239990792Sgshapiro					     "REPLACED");
240064562Sgshapiro			break;
240164562Sgshapiro
240264562Sgshapiro		  case QS_REMOVED:
2403132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
240490792Sgshapiro					     "REMOVED");
240564562Sgshapiro			break;
240664562Sgshapiro
240764562Sgshapiro		  case QS_DUPLICATE:
2408132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
240990792Sgshapiro					     "DUPLICATE");
241064562Sgshapiro			break;
241164562Sgshapiro
241264562Sgshapiro		  case QS_INCLUDED:
2413132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
241490792Sgshapiro					     "INCLUDED");
241564562Sgshapiro			break;
241664562Sgshapiro
241764562Sgshapiro		  default:
2418132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
241990792Sgshapiro					     "%d", a->q_state);
242064562Sgshapiro			break;
242164562Sgshapiro		}
2422132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
242390792Sgshapiro				     ", next=%p, alias %p, uid %d, gid %d\n",
242490792Sgshapiro				     a->q_next, a->q_alias,
242590792Sgshapiro				     (int) a->q_uid, (int) a->q_gid);
2426132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<",
242790792Sgshapiro				     a->q_flags);
242890792Sgshapiro		firstone = true;
242938032Speter		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
243038032Speter		{
243138032Speter			if (!bitset(qfp->qf_bit, a->q_flags))
243238032Speter				continue;
243338032Speter			if (!firstone)
2434132943Sgshapiro				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
243590792Sgshapiro						     ",");
243690792Sgshapiro			firstone = false;
2437132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
243890792Sgshapiro					     qfp->qf_name);
243938032Speter		}
2440132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n");
2441132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
244290792Sgshapiro				     "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
244390792Sgshapiro				     a->q_owner == NULL ? "(none)" : a->q_owner,
244490792Sgshapiro				     a->q_home == NULL ? "(none)" : a->q_home,
244590792Sgshapiro				     a->q_fullname == NULL ? "(none)" : a->q_fullname);
2446132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
244790792Sgshapiro				     "\torcpt=\"%s\", statmta=%s, status=%s\n",
244890792Sgshapiro				     a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
244990792Sgshapiro				     a->q_statmta == NULL ? "(none)" : a->q_statmta,
245090792Sgshapiro				     a->q_status == NULL ? "(none)" : a->q_status);
2451132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
245290792Sgshapiro				     "\tfinalrcpt=\"%s\"\n",
245390792Sgshapiro				     a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
2454132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
245590792Sgshapiro				     "\trstatus=\"%s\"\n",
245690792Sgshapiro				     a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2457132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
245890792Sgshapiro				     "\tstatdate=%s\n",
245990792Sgshapiro				     a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
246038032Speter
246138032Speter		if (!follow)
246238032Speter			return;
246338032Speter		a = a->q_next;
246438032Speter	}
246538032Speter}
246690792Sgshapiro/*
246790792Sgshapiro**  EMPTYADDR -- return true if this address is empty (``<>'')
246838032Speter**
246938032Speter**	Parameters:
247038032Speter**		a -- pointer to the address
247138032Speter**
247238032Speter**	Returns:
247390792Sgshapiro**		true -- if this address is "empty" (i.e., no one should
247438032Speter**			ever generate replies to it.
247590792Sgshapiro**		false -- if it is a "regular" (read: replyable) address.
247638032Speter*/
247738032Speter
247838032Speterbool
247938032Speteremptyaddr(a)
248038032Speter	register ADDRESS *a;
248138032Speter{
248238032Speter	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
248338032Speter	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
248438032Speter}
248590792Sgshapiro/*
248638032Speter**  REMOTENAME -- return the name relative to the current mailer
248738032Speter**
248838032Speter**	Parameters:
248938032Speter**		name -- the name to translate.
2490120256Sgshapiro**		m -- the mailer that we want to do rewriting relative to.
249138032Speter**		flags -- fine tune operations.
249238032Speter**		pstat -- pointer to status word.
249338032Speter**		e -- the current envelope.
249438032Speter**
249538032Speter**	Returns:
249638032Speter**		the text string representing this address relative to
249738032Speter**			the receiving mailer.
249838032Speter**
249938032Speter**	Side Effects:
250038032Speter**		none.
250138032Speter**
250238032Speter**	Warnings:
250338032Speter**		The text string returned is tucked away locally;
250438032Speter**			copy it if you intend to save it.
250538032Speter*/
250638032Speter
250738032Speterchar *
250838032Speterremotename(name, m, flags, pstat, e)
250938032Speter	char *name;
251038032Speter	struct mailer *m;
251138032Speter	int flags;
251238032Speter	int *pstat;
251338032Speter	register ENVELOPE *e;
251438032Speter{
251538032Speter	register char **pvp;
251690792Sgshapiro	char *SM_NONVOLATILE fancy;
251790792Sgshapiro	char *oldg;
251838032Speter	int rwset;
251938032Speter	static char buf[MAXNAME + 1];
252038032Speter	char lbuf[MAXNAME + 1];
252138032Speter	char pvpbuf[PSBUFSIZE];
252264562Sgshapiro	char addrtype[4];
252338032Speter
252438032Speter	if (tTd(12, 1))
252590792Sgshapiro		sm_dprintf("remotename(%s)\n", name);
252638032Speter
252738032Speter	/* don't do anything if we are tagging it as special */
252838032Speter	if (bitset(RF_SENDERADDR, flags))
252964562Sgshapiro	{
253038032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
253138032Speter						     : m->m_se_rwset;
253264562Sgshapiro		addrtype[2] = 's';
253364562Sgshapiro	}
253438032Speter	else
253564562Sgshapiro	{
253638032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
253738032Speter						     : m->m_re_rwset;
253864562Sgshapiro		addrtype[2] = 'r';
253964562Sgshapiro	}
254038032Speter	if (rwset < 0)
254164562Sgshapiro		return name;
254264562Sgshapiro	addrtype[1] = ' ';
254364562Sgshapiro	addrtype[3] = '\0';
254464562Sgshapiro	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
254590792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
254638032Speter
254738032Speter	/*
254838032Speter	**  Do a heuristic crack of this name to extract any comment info.
254938032Speter	**	This will leave the name as a comment and a $g macro.
255038032Speter	*/
255138032Speter
255238032Speter	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
255338032Speter		fancy = "\201g";
255438032Speter	else
2555111823Sgshapiro		fancy = crackaddr(name, e);
255638032Speter
255738032Speter	/*
255838032Speter	**  Turn the name into canonical form.
255938032Speter	**	Normally this will be RFC 822 style, i.e., "user@domain".
256038032Speter	**	If this only resolves to "user", and the "C" flag is
256138032Speter	**	specified in the sending mailer, then the sender's
256238032Speter	**	domain will be appended.
256338032Speter	*/
256438032Speter
2565132943Sgshapiro	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL, false);
256638032Speter	if (pvp == NULL)
256764562Sgshapiro		return name;
256890792Sgshapiro	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
256938032Speter		*pstat = EX_TEMPFAIL;
257038032Speter	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
257138032Speter	{
257238032Speter		/* append from domain to this address */
257338032Speter		register char **pxp = pvp;
257464562Sgshapiro		int l = MAXATOM;	/* size of buffer for pvp */
257538032Speter
257638032Speter		/* see if there is an "@domain" in the current name */
257738032Speter		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
257864562Sgshapiro		{
257938032Speter			pxp++;
258064562Sgshapiro			--l;
258164562Sgshapiro		}
258238032Speter		if (*pxp == NULL)
258338032Speter		{
258438032Speter			/* no.... append the "@domain" from the sender */
258538032Speter			register char **qxq = e->e_fromdomain;
258638032Speter
258738032Speter			while ((*pxp++ = *qxq++) != NULL)
258864562Sgshapiro			{
258964562Sgshapiro				if (--l <= 0)
259064562Sgshapiro				{
259164562Sgshapiro					*--pxp = NULL;
259264562Sgshapiro					usrerr("553 5.1.0 remotename: too many tokens");
259364562Sgshapiro					*pstat = EX_UNAVAILABLE;
259464562Sgshapiro					break;
259564562Sgshapiro				}
259664562Sgshapiro			}
259790792Sgshapiro			if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
259838032Speter				*pstat = EX_TEMPFAIL;
259938032Speter		}
260038032Speter	}
260138032Speter
260238032Speter	/*
260338032Speter	**  Do more specific rewriting.
260438032Speter	**	Rewrite using ruleset 1 or 2 depending on whether this is
260538032Speter	**		a sender address or not.
260638032Speter	**	Then run it through any receiving-mailer-specific rulesets.
260738032Speter	*/
260838032Speter
260938032Speter	if (bitset(RF_SENDERADDR, flags))
261038032Speter	{
261190792Sgshapiro		if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
261238032Speter			*pstat = EX_TEMPFAIL;
261338032Speter	}
261438032Speter	else
261538032Speter	{
261690792Sgshapiro		if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
261738032Speter			*pstat = EX_TEMPFAIL;
261838032Speter	}
261938032Speter	if (rwset > 0)
262038032Speter	{
262190792Sgshapiro		if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
262238032Speter			*pstat = EX_TEMPFAIL;
262338032Speter	}
262438032Speter
262538032Speter	/*
262638032Speter	**  Do any final sanitation the address may require.
262738032Speter	**	This will normally be used to turn internal forms
262838032Speter	**	(e.g., user@host.LOCAL) into external form.  This
262938032Speter	**	may be used as a default to the above rules.
263038032Speter	*/
263138032Speter
263290792Sgshapiro	if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
263338032Speter		*pstat = EX_TEMPFAIL;
263438032Speter
263538032Speter	/*
263638032Speter	**  Now restore the comment information we had at the beginning.
263738032Speter	*/
263838032Speter
263938032Speter	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
264090792Sgshapiro	oldg = macget(&e->e_macro, 'g');
264190792Sgshapiro	macset(&e->e_macro, 'g', lbuf);
264238032Speter
264390792Sgshapiro	SM_TRY
264490792Sgshapiro		/* need to make sure route-addrs have <angle brackets> */
264590792Sgshapiro		if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
264690792Sgshapiro			expand("<\201g>", buf, sizeof buf, e);
264790792Sgshapiro		else
264890792Sgshapiro			expand(fancy, buf, sizeof buf, e);
264990792Sgshapiro	SM_FINALLY
265090792Sgshapiro		macset(&e->e_macro, 'g', oldg);
265190792Sgshapiro	SM_END_TRY
265238032Speter
265338032Speter	if (tTd(12, 1))
265490792Sgshapiro		sm_dprintf("remotename => `%s'\n", buf);
265564562Sgshapiro	return buf;
265638032Speter}
265790792Sgshapiro/*
265838032Speter**  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
265938032Speter**
266038032Speter**	Parameters:
266138032Speter**		a -- the address to map (but just the user name part).
266238032Speter**		sendq -- the sendq in which to install any replacement
266338032Speter**			addresses.
266438032Speter**		aliaslevel -- the alias nesting depth.
266538032Speter**		e -- the envelope.
266638032Speter**
266738032Speter**	Returns:
266838032Speter**		none.
266938032Speter*/
267038032Speter
267138032Speter#define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
267238032Speter			 Q_PINGFLAGS|QHASNOTIFY|\
267390792Sgshapiro			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
267490792Sgshapiro			 QBYTRACE|QBYNDELAY|QBYNRELAY)
267538032Speter
267638032Spetervoid
267738032Spetermaplocaluser(a, sendq, aliaslevel, e)
267838032Speter	register ADDRESS *a;
267938032Speter	ADDRESS **sendq;
268038032Speter	int aliaslevel;
268138032Speter	ENVELOPE *e;
268238032Speter{
268338032Speter	register char **pvp;
268490792Sgshapiro	register ADDRESS *SM_NONVOLATILE a1 = NULL;
268538032Speter	char pvpbuf[PSBUFSIZE];
268638032Speter
268738032Speter	if (tTd(29, 1))
268838032Speter	{
268990792Sgshapiro		sm_dprintf("maplocaluser: ");
2690132943Sgshapiro		printaddr(sm_debug_file(), a, false);
269138032Speter	}
2692132943Sgshapiro	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL, false);
269338032Speter	if (pvp == NULL)
269438032Speter	{
269538032Speter		if (tTd(29, 9))
269690792Sgshapiro			sm_dprintf("maplocaluser: cannot prescan %s\n",
269764562Sgshapiro				a->q_user);
269838032Speter		return;
269938032Speter	}
270038032Speter
270190792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
270290792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
270390792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
270464562Sgshapiro
270590792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
270690792Sgshapiro	if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
270738032Speter	{
270838032Speter		if (tTd(29, 9))
270990792Sgshapiro			sm_dprintf("maplocaluser: rewrite tempfail\n");
271064562Sgshapiro		a->q_state = QS_QUEUEUP;
271138032Speter		a->q_status = "4.4.3";
271238032Speter		return;
271338032Speter	}
271438032Speter	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
271538032Speter	{
271638032Speter		if (tTd(29, 9))
271790792Sgshapiro			sm_dprintf("maplocaluser: doesn't resolve\n");
271838032Speter		return;
271938032Speter	}
272038032Speter
272190792Sgshapiro	SM_TRY
272290792Sgshapiro		a1 = buildaddr(pvp, NULL, 0, e);
272390792Sgshapiro	SM_EXCEPT(exc, "E:mta.quickabort")
272490792Sgshapiro
272590792Sgshapiro		/*
272690792Sgshapiro		**  mark address as bad, S5 returned an error
272790792Sgshapiro		**	and we gave that back to the SMTP client.
272890792Sgshapiro		*/
272990792Sgshapiro
273090792Sgshapiro		a->q_state = QS_DONTSEND;
273190792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
273290792Sgshapiro	SM_END_TRY
273390792Sgshapiro
273438032Speter	/* if non-null, mailer destination specified -- has it changed? */
273538032Speter	if (a1 == NULL || sameaddr(a, a1))
273638032Speter	{
273738032Speter		if (tTd(29, 9))
273890792Sgshapiro			sm_dprintf("maplocaluser: address unchanged\n");
273938032Speter		return;
274038032Speter	}
274138032Speter
274238032Speter	/* make new address take on flags and print attributes of old */
274338032Speter	a1->q_flags &= ~Q_COPYFLAGS;
274438032Speter	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
274590792Sgshapiro	a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
274690792Sgshapiro	a1->q_finalrcpt = a->q_finalrcpt;
274764562Sgshapiro	a1->q_orcpt = a->q_orcpt;
274838032Speter
274938032Speter	/* mark old address as dead; insert new address */
275064562Sgshapiro	a->q_state = QS_REPLACED;
275138032Speter	if (tTd(29, 5))
275238032Speter	{
275390792Sgshapiro		sm_dprintf("maplocaluser: QS_REPLACED ");
2754132943Sgshapiro		printaddr(sm_debug_file(), a, false);
275538032Speter	}
275638032Speter	a1->q_alias = a;
275790792Sgshapiro	allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
275838032Speter	(void) recipient(a1, sendq, aliaslevel, e);
275938032Speter}
276090792Sgshapiro/*
276138032Speter**  DEQUOTE_INIT -- initialize dequote map
276238032Speter**
276338032Speter**	Parameters:
276438032Speter**		map -- the internal map structure.
276538032Speter**		args -- arguments.
276638032Speter**
276738032Speter**	Returns:
276890792Sgshapiro**		true.
276938032Speter*/
277038032Speter
277138032Speterbool
277238032Speterdequote_init(map, args)
277338032Speter	MAP *map;
277438032Speter	char *args;
277538032Speter{
277638032Speter	register char *p = args;
277738032Speter
277864562Sgshapiro	/* there is no check whether there is really an argument */
277938032Speter	map->map_mflags |= MF_KEEPQUOTES;
278038032Speter	for (;;)
278138032Speter	{
278238032Speter		while (isascii(*p) && isspace(*p))
278338032Speter			p++;
278438032Speter		if (*p != '-')
278538032Speter			break;
278638032Speter		switch (*++p)
278738032Speter		{
278838032Speter		  case 'a':
278938032Speter			map->map_app = ++p;
279038032Speter			break;
279138032Speter
279264562Sgshapiro		  case 'D':
279364562Sgshapiro			map->map_mflags |= MF_DEFER;
279464562Sgshapiro			break;
279564562Sgshapiro
279664562Sgshapiro		  case 'S':
279738032Speter		  case 's':
279864562Sgshapiro			map->map_spacesub = *++p;
279938032Speter			break;
280038032Speter		}
280138032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
280238032Speter			p++;
280338032Speter		if (*p != '\0')
280438032Speter			*p = '\0';
280538032Speter	}
280638032Speter	if (map->map_app != NULL)
280738032Speter		map->map_app = newstr(map->map_app);
280838032Speter
280990792Sgshapiro	return true;
281038032Speter}
281190792Sgshapiro/*
281238032Speter**  DEQUOTE_MAP -- unquote an address
281338032Speter**
281438032Speter**	Parameters:
281538032Speter**		map -- the internal map structure (ignored).
281638032Speter**		name -- the name to dequote.
281738032Speter**		av -- arguments (ignored).
281838032Speter**		statp -- pointer to status out-parameter.
281938032Speter**
282038032Speter**	Returns:
282138032Speter**		NULL -- if there were no quotes, or if the resulting
282238032Speter**			unquoted buffer would not be acceptable to prescan.
282338032Speter**		else -- The dequoted buffer.
282438032Speter*/
282538032Speter
282638032Speter/* ARGSUSED2 */
282738032Speterchar *
282838032Speterdequote_map(map, name, av, statp)
282938032Speter	MAP *map;
283038032Speter	char *name;
283138032Speter	char **av;
283238032Speter	int *statp;
283338032Speter{
283438032Speter	register char *p;
283538032Speter	register char *q;
283638032Speter	register char c;
283738032Speter	int anglecnt = 0;
283838032Speter	int cmntcnt = 0;
283938032Speter	int quotecnt = 0;
284038032Speter	int spacecnt = 0;
284190792Sgshapiro	bool quotemode = false;
284290792Sgshapiro	bool bslashmode = false;
284364562Sgshapiro	char spacesub = map->map_spacesub;
284438032Speter
284538032Speter	for (p = q = name; (c = *p++) != '\0'; )
284638032Speter	{
284738032Speter		if (bslashmode)
284838032Speter		{
284990792Sgshapiro			bslashmode = false;
285038032Speter			*q++ = c;
285138032Speter			continue;
285238032Speter		}
285338032Speter
285438032Speter		if (c == ' ' && spacesub != '\0')
285538032Speter			c = spacesub;
285638032Speter
285738032Speter		switch (c)
285838032Speter		{
285938032Speter		  case '\\':
286090792Sgshapiro			bslashmode = true;
286138032Speter			break;
286238032Speter
286338032Speter		  case '(':
286438032Speter			cmntcnt++;
286538032Speter			break;
286638032Speter
286738032Speter		  case ')':
286838032Speter			if (cmntcnt-- <= 0)
286938032Speter				return NULL;
287038032Speter			break;
287138032Speter
287238032Speter		  case ' ':
287364562Sgshapiro		  case '\t':
287438032Speter			spacecnt++;
287538032Speter			break;
287638032Speter		}
287738032Speter
287838032Speter		if (cmntcnt > 0)
287938032Speter		{
288038032Speter			*q++ = c;
288138032Speter			continue;
288238032Speter		}
288338032Speter
288438032Speter		switch (c)
288538032Speter		{
288638032Speter		  case '"':
288738032Speter			quotemode = !quotemode;
288838032Speter			quotecnt++;
288938032Speter			continue;
289038032Speter
289138032Speter		  case '<':
289238032Speter			anglecnt++;
289338032Speter			break;
289438032Speter
289538032Speter		  case '>':
289638032Speter			if (anglecnt-- <= 0)
289738032Speter				return NULL;
289838032Speter			break;
289938032Speter		}
290038032Speter		*q++ = c;
290138032Speter	}
290238032Speter
290338032Speter	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
290438032Speter	    quotemode || quotecnt <= 0 || spacecnt != 0)
290538032Speter		return NULL;
290638032Speter	*q++ = '\0';
290738032Speter	return map_rewrite(map, name, strlen(name), NULL);
290838032Speter}
290990792Sgshapiro/*
291038032Speter**  RSCHECK -- check string(s) for validity using rewriting sets
291138032Speter**
291238032Speter**	Parameters:
291338032Speter**		rwset -- the rewriting set to use.
291438032Speter**		p1 -- the first string to check.
291538032Speter**		p2 -- the second string to check -- may be null.
291638032Speter**		e -- the current envelope.
2917102528Sgshapiro**		flags -- control some behavior, see RSF_ in sendmail.h
291890792Sgshapiro**		logl -- logging level.
291971345Sgshapiro**		host -- NULL or relay host.
292090792Sgshapiro**		logid -- id for sm_syslog.
292138032Speter**
292238032Speter**	Returns:
292338032Speter**		EX_OK -- if the rwset doesn't resolve to $#error
292438032Speter**		else -- the failure status (message printed)
292538032Speter*/
292638032Speter
292738032Speterint
2928102528Sgshapirorscheck(rwset, p1, p2, e, flags, logl, host, logid)
292938032Speter	char *rwset;
293038032Speter	char *p1;
293138032Speter	char *p2;
293238032Speter	ENVELOPE *e;
2933102528Sgshapiro	int flags;
293464562Sgshapiro	int logl;
293571345Sgshapiro	char *host;
293690792Sgshapiro	char *logid;
293738032Speter{
293890792Sgshapiro	char *volatile buf;
293938032Speter	int bufsize;
294038032Speter	int saveexitstat;
294190792Sgshapiro	int volatile rstat = EX_OK;
294238032Speter	char **pvp;
294338032Speter	int rsno;
294490792Sgshapiro	bool volatile discard = false;
294538032Speter	auto ADDRESS a1;
294638032Speter	bool saveQuickAbort = QuickAbort;
294738032Speter	bool saveSuprErrs = SuprErrs;
294890792Sgshapiro	bool quarantine = false;
294990792Sgshapiro	char ubuf[BUFSIZ * 2];
295038032Speter	char buf0[MAXLINE];
295138032Speter	char pvpbuf[PSBUFSIZE];
295238032Speter	extern char MsgBuf[];
295338032Speter
295438032Speter	if (tTd(48, 2))
295590792Sgshapiro		sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
295638032Speter			p2 == NULL ? "(NULL)" : p2);
295738032Speter
295838032Speter	rsno = strtorwset(rwset, NULL, ST_FIND);
295938032Speter	if (rsno < 0)
296038032Speter		return EX_OK;
296138032Speter
296238032Speter	if (p2 != NULL)
296338032Speter	{
296438032Speter		bufsize = strlen(p1) + strlen(p2) + 2;
296538032Speter		if (bufsize > sizeof buf0)
296690792Sgshapiro			buf = sm_malloc_x(bufsize);
296738032Speter		else
296838032Speter		{
296938032Speter			buf = buf0;
297038032Speter			bufsize = sizeof buf0;
297138032Speter		}
297290792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
297338032Speter	}
297438032Speter	else
297538032Speter	{
297638032Speter		bufsize = strlen(p1) + 1;
297738032Speter		if (bufsize > sizeof buf0)
297890792Sgshapiro			buf = sm_malloc_x(bufsize);
297938032Speter		else
298038032Speter		{
298138032Speter			buf = buf0;
298238032Speter			bufsize = sizeof buf0;
298338032Speter		}
298490792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
298538032Speter	}
298690792Sgshapiro	SM_TRY
298738032Speter	{
298890792Sgshapiro		SuprErrs = true;
298990792Sgshapiro		QuickAbort = false;
299090792Sgshapiro		pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
2991132943Sgshapiro			      bitset(RSF_RMCOMM, flags) ? NULL : TokTypeNoC,
2992132943Sgshapiro			      bitset(RSF_RMCOMM, flags) ? false : true);
299390792Sgshapiro		SuprErrs = saveSuprErrs;
299490792Sgshapiro		if (pvp == NULL)
299590792Sgshapiro		{
299690792Sgshapiro			if (tTd(48, 2))
299790792Sgshapiro				sm_dprintf("rscheck: cannot prescan input\n");
299890792Sgshapiro	/*
299990792Sgshapiro			syserr("rscheck: cannot prescan input: \"%s\"",
300090792Sgshapiro				shortenstring(buf, MAXSHORTSTR));
300190792Sgshapiro			rstat = EX_DATAERR;
300290792Sgshapiro	*/
300390792Sgshapiro			goto finis;
300490792Sgshapiro		}
3005102528Sgshapiro		if (bitset(RSF_UNSTRUCTURED, flags))
3006102528Sgshapiro			SuprErrs = true;
300790792Sgshapiro		(void) REWRITE(pvp, rsno, e);
3008102528Sgshapiro		if (bitset(RSF_UNSTRUCTURED, flags))
3009102528Sgshapiro			SuprErrs = saveSuprErrs;
301090792Sgshapiro		if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
301190792Sgshapiro		    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
301290792Sgshapiro				       strcmp(pvp[1], "discard") != 0))
301390792Sgshapiro		{
301490792Sgshapiro			goto finis;
301590792Sgshapiro		}
301666494Sgshapiro
301790792Sgshapiro		if (strcmp(pvp[1], "discard") == 0)
301890792Sgshapiro		{
301990792Sgshapiro			if (tTd(48, 2))
302090792Sgshapiro				sm_dprintf("rscheck: discard mailer selected\n");
302190792Sgshapiro			e->e_flags |= EF_DISCARD;
302290792Sgshapiro			discard = true;
302390792Sgshapiro		}
302490792Sgshapiro		else if (strcmp(pvp[1], "error") == 0 &&
302590792Sgshapiro			 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
302690792Sgshapiro			 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
302790792Sgshapiro		{
302890792Sgshapiro			if (pvp[4] == NULL ||
302990792Sgshapiro			    (pvp[4][0] & 0377) != CANONUSER ||
303090792Sgshapiro			    pvp[5] == NULL)
303190792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
303290792Sgshapiro								 rwset);
303390792Sgshapiro			else
303490792Sgshapiro			{
303590792Sgshapiro				cataddr(&(pvp[5]), NULL, ubuf,
303690792Sgshapiro					sizeof ubuf, ' ');
303790792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
303890792Sgshapiro								 ubuf);
303990792Sgshapiro			}
304090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
304190792Sgshapiro				  macid("{quarantine}"), e->e_quarmsg);
304290792Sgshapiro			quarantine = true;
304390792Sgshapiro		}
304490792Sgshapiro		else
304590792Sgshapiro		{
304690792Sgshapiro			int savelogusrerrs = LogUsrErrs;
304790792Sgshapiro			static bool logged = false;
304890792Sgshapiro
304990792Sgshapiro			/* got an error -- process it */
305090792Sgshapiro			saveexitstat = ExitStat;
305190792Sgshapiro			LogUsrErrs = false;
305290792Sgshapiro			(void) buildaddr(pvp, &a1, 0, e);
305390792Sgshapiro			LogUsrErrs = savelogusrerrs;
305490792Sgshapiro			rstat = ExitStat;
305590792Sgshapiro			ExitStat = saveexitstat;
305690792Sgshapiro			if (!logged)
305790792Sgshapiro			{
3058102528Sgshapiro				if (bitset(RSF_COUNT, flags))
305990792Sgshapiro					markstats(e, &a1, STATS_REJECT);
306090792Sgshapiro				logged = true;
306190792Sgshapiro			}
306290792Sgshapiro		}
306390792Sgshapiro
306490792Sgshapiro		if (LogLevel > logl)
306590792Sgshapiro		{
306690792Sgshapiro			char *relay;
306790792Sgshapiro			char *p;
306890792Sgshapiro			char lbuf[MAXLINE];
306990792Sgshapiro
307090792Sgshapiro			p = lbuf;
307190792Sgshapiro			if (p2 != NULL)
307290792Sgshapiro			{
307390792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
307490792Sgshapiro					", arg2=%s",
307590792Sgshapiro					p2);
307690792Sgshapiro				p += strlen(p);
307790792Sgshapiro			}
307890792Sgshapiro
307990792Sgshapiro			if (host != NULL)
308090792Sgshapiro				relay = host;
308190792Sgshapiro			else
308290792Sgshapiro				relay = macvalue('_', e);
308390792Sgshapiro			if (relay != NULL)
308490792Sgshapiro			{
308590792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
308690792Sgshapiro					", relay=%s", relay);
308790792Sgshapiro				p += strlen(p);
308890792Sgshapiro			}
308990792Sgshapiro			*p = '\0';
309090792Sgshapiro			if (discard)
309190792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
309290792Sgshapiro					  "ruleset=%s, arg1=%s%s, discard",
309390792Sgshapiro					  rwset, p1, lbuf);
309490792Sgshapiro			else if (quarantine)
309590792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
309690792Sgshapiro					  "ruleset=%s, arg1=%s%s, quarantine=%s",
309790792Sgshapiro					  rwset, p1, lbuf, ubuf);
309890792Sgshapiro			else
309990792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
310090792Sgshapiro					  "ruleset=%s, arg1=%s%s, reject=%s",
310190792Sgshapiro					  rwset, p1, lbuf, MsgBuf);
310290792Sgshapiro		}
310390792Sgshapiro
310490792Sgshapiro	 finis: ;
310573188Sgshapiro	}
310690792Sgshapiro	SM_FINALLY
310738032Speter	{
310890792Sgshapiro		/* clean up */
310990792Sgshapiro		if (buf != buf0)
311090792Sgshapiro			sm_free(buf);
311190792Sgshapiro		QuickAbort = saveQuickAbort;
311238032Speter	}
311390792Sgshapiro	SM_END_TRY
311438032Speter
311590792Sgshapiro	setstat(rstat);
311690792Sgshapiro
311790792Sgshapiro	/* rulesets don't set errno */
311890792Sgshapiro	errno = 0;
311990792Sgshapiro	if (rstat != EX_OK && QuickAbort)
312090792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
312190792Sgshapiro	return rstat;
312290792Sgshapiro}
312390792Sgshapiro/*
312490792Sgshapiro**  RSCAP -- call rewriting set to return capabilities
312590792Sgshapiro**
312690792Sgshapiro**	Parameters:
312790792Sgshapiro**		rwset -- the rewriting set to use.
312890792Sgshapiro**		p1 -- the first string to check.
312990792Sgshapiro**		p2 -- the second string to check -- may be null.
313090792Sgshapiro**		e -- the current envelope.
313190792Sgshapiro**		pvp -- pointer to token vector.
313290792Sgshapiro**		pvpbuf -- buffer space.
3133120256Sgshapiro**		size -- size of buffer space.
313490792Sgshapiro**
313590792Sgshapiro**	Returns:
313690792Sgshapiro**		EX_UNAVAILABLE -- ruleset doesn't exist.
313790792Sgshapiro**		EX_DATAERR -- prescan() failed.
313890792Sgshapiro**		EX_OK -- rewrite() was successful.
313990792Sgshapiro**		else -- return status from rewrite().
314090792Sgshapiro*/
314190792Sgshapiro
314290792Sgshapiroint
314390792Sgshapirorscap(rwset, p1, p2, e, pvp, pvpbuf, size)
314490792Sgshapiro	char *rwset;
314590792Sgshapiro	char *p1;
314690792Sgshapiro	char *p2;
314790792Sgshapiro	ENVELOPE *e;
314890792Sgshapiro	char ***pvp;
314990792Sgshapiro	char *pvpbuf;
315090792Sgshapiro	int size;
315190792Sgshapiro{
315290792Sgshapiro	char *volatile buf;
315390792Sgshapiro	int bufsize;
315490792Sgshapiro	int volatile rstat = EX_OK;
315590792Sgshapiro	int rsno;
315690792Sgshapiro	bool saveQuickAbort = QuickAbort;
315790792Sgshapiro	bool saveSuprErrs = SuprErrs;
315890792Sgshapiro	char buf0[MAXLINE];
315990792Sgshapiro	extern char MsgBuf[];
316090792Sgshapiro
316190792Sgshapiro	if (tTd(48, 2))
316290792Sgshapiro		sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
316390792Sgshapiro			p2 == NULL ? "(NULL)" : p2);
316490792Sgshapiro
316590792Sgshapiro	if (pvp != NULL)
316690792Sgshapiro		*pvp = NULL;
316790792Sgshapiro	rsno = strtorwset(rwset, NULL, ST_FIND);
316890792Sgshapiro	if (rsno < 0)
316990792Sgshapiro		return EX_UNAVAILABLE;
317090792Sgshapiro
317190792Sgshapiro	if (p2 != NULL)
317238032Speter	{
317390792Sgshapiro		bufsize = strlen(p1) + strlen(p2) + 2;
317490792Sgshapiro		if (bufsize > sizeof buf0)
317590792Sgshapiro			buf = sm_malloc_x(bufsize);
317690792Sgshapiro		else
317790792Sgshapiro		{
317890792Sgshapiro			buf = buf0;
317990792Sgshapiro			bufsize = sizeof buf0;
318090792Sgshapiro		}
318190792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
318238032Speter	}
318364562Sgshapiro	else
318438032Speter	{
318590792Sgshapiro		bufsize = strlen(p1) + 1;
318690792Sgshapiro		if (bufsize > sizeof buf0)
318790792Sgshapiro			buf = sm_malloc_x(bufsize);
318890792Sgshapiro		else
318938032Speter		{
319090792Sgshapiro			buf = buf0;
319190792Sgshapiro			bufsize = sizeof buf0;
319238032Speter		}
319390792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
319438032Speter	}
319590792Sgshapiro	SM_TRY
319638032Speter	{
319790792Sgshapiro		SuprErrs = true;
319890792Sgshapiro		QuickAbort = false;
3199132943Sgshapiro		*pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL, false);
320090792Sgshapiro		if (*pvp != NULL)
3201120256Sgshapiro			rstat = rewrite(*pvp, rsno, 0, e, size);
320271345Sgshapiro		else
320338032Speter		{
320490792Sgshapiro			if (tTd(48, 2))
320590792Sgshapiro				sm_dprintf("rscap: cannot prescan input\n");
320690792Sgshapiro			rstat = EX_DATAERR;
320738032Speter		}
320838032Speter	}
320990792Sgshapiro	SM_FINALLY
321090792Sgshapiro	{
321190792Sgshapiro		/* clean up */
321290792Sgshapiro		if (buf != buf0)
321390792Sgshapiro			sm_free(buf);
321490792Sgshapiro		SuprErrs = saveSuprErrs;
321590792Sgshapiro		QuickAbort = saveQuickAbort;
321638032Speter
321790792Sgshapiro		/* prevent information leak, this may contain rewrite error */
321890792Sgshapiro		MsgBuf[0] = '\0';
321990792Sgshapiro	}
322090792Sgshapiro	SM_END_TRY
322138032Speter	return rstat;
322238032Speter}
3223