parseaddr.c revision 147078
138032Speter/*
2147078Sgshapiro * Copyright (c) 1998-2005 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
16147078SgshapiroSM_RCSID("@(#)$Id: parseaddr.c,v 8.381 2005/02/04 22:01:45 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
2197147078Sgshapiro#if 0
2198147078Sgshapiro	/*
2199147078Sgshapiro	**  Silently truncate long strings: even though this doesn't
2200147078Sgshapiro	**  seem like a good idea it is necessary because header checks
2201147078Sgshapiro	**  send the whole header value to rscheck() and hence rewrite().
2202147078Sgshapiro	**  The latter however sometimes uses a "short" buffer (e.g.,
2203147078Sgshapiro	**  cbuf[MAXNAME + 1]) to call cataddr() which then triggers this
2204147078Sgshapiro	**  error function.  One possible fix to the problem is to pass
2205147078Sgshapiro	**  flags to rscheck() and rewrite() to distinguish the various
2206147078Sgshapiro	**  calls and only trigger the error if necessary.  For now just
2207147078Sgshapiro	**  undo the change from 8.13.0.
2208147078Sgshapiro	*/
2209147078Sgshapiro
2210132943Sgshapiro	if (sz <= 0)
2211141858Sgshapiro		usrerr("cataddr: string too long");
2212147078Sgshapiro#endif
221338032Speter	*p = '\0';
221438032Speter}
221590792Sgshapiro/*
221638032Speter**  SAMEADDR -- Determine if two addresses are the same
221738032Speter**
221838032Speter**	This is not just a straight comparison -- if the mailer doesn't
221938032Speter**	care about the host we just ignore it, etc.
222038032Speter**
222138032Speter**	Parameters:
222238032Speter**		a, b -- pointers to the internal forms to compare.
222338032Speter**
222438032Speter**	Returns:
222590792Sgshapiro**		true -- they represent the same mailbox.
222690792Sgshapiro**		false -- they don't.
222738032Speter**
222838032Speter**	Side Effects:
222938032Speter**		none.
223038032Speter*/
223138032Speter
223238032Speterbool
223338032Spetersameaddr(a, b)
223438032Speter	register ADDRESS *a;
223538032Speter	register ADDRESS *b;
223638032Speter{
223738032Speter	register ADDRESS *ca, *cb;
223838032Speter
223938032Speter	/* if they don't have the same mailer, forget it */
224038032Speter	if (a->q_mailer != b->q_mailer)
224190792Sgshapiro		return false;
224238032Speter
224338032Speter	/* if the user isn't the same, we can drop out */
224438032Speter	if (strcmp(a->q_user, b->q_user) != 0)
224590792Sgshapiro		return false;
224638032Speter
224738032Speter	/* if we have good uids for both but they differ, these are different */
224838032Speter	if (a->q_mailer == ProgMailer)
224938032Speter	{
225038032Speter		ca = getctladdr(a);
225138032Speter		cb = getctladdr(b);
225238032Speter		if (ca != NULL && cb != NULL &&
225338032Speter		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
225438032Speter		    ca->q_uid != cb->q_uid)
225590792Sgshapiro			return false;
225638032Speter	}
225738032Speter
225838032Speter	/* otherwise compare hosts (but be careful for NULL ptrs) */
225938032Speter	if (a->q_host == b->q_host)
226038032Speter	{
226138032Speter		/* probably both null pointers */
226290792Sgshapiro		return true;
226338032Speter	}
226438032Speter	if (a->q_host == NULL || b->q_host == NULL)
226538032Speter	{
226638032Speter		/* only one is a null pointer */
226790792Sgshapiro		return false;
226838032Speter	}
226938032Speter	if (strcmp(a->q_host, b->q_host) != 0)
227090792Sgshapiro		return false;
227138032Speter
227290792Sgshapiro	return true;
227338032Speter}
227490792Sgshapiro/*
227538032Speter**  PRINTADDR -- print address (for debugging)
227638032Speter**
227738032Speter**	Parameters:
227838032Speter**		a -- the address to print
227938032Speter**		follow -- follow the q_next chain.
228038032Speter**
228138032Speter**	Returns:
228238032Speter**		none.
228338032Speter**
228438032Speter**	Side Effects:
228538032Speter**		none.
228638032Speter*/
228738032Speter
228838032Speterstruct qflags
228938032Speter{
229090792Sgshapiro	char		*qf_name;
229190792Sgshapiro	unsigned long	qf_bit;
229238032Speter};
229338032Speter
229464562Sgshapirostatic struct qflags	AddressFlags[] =
229538032Speter{
229638032Speter	{ "QGOODUID",		QGOODUID	},
229738032Speter	{ "QPRIMARY",		QPRIMARY	},
229838032Speter	{ "QNOTREMOTE",		QNOTREMOTE	},
229938032Speter	{ "QSELFREF",		QSELFREF	},
230038032Speter	{ "QBOGUSSHELL",	QBOGUSSHELL	},
230138032Speter	{ "QUNSAFEADDR",	QUNSAFEADDR	},
230238032Speter	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
230338032Speter	{ "QPINGONFAILURE",	QPINGONFAILURE	},
230438032Speter	{ "QPINGONDELAY",	QPINGONDELAY	},
230538032Speter	{ "QHASNOTIFY",		QHASNOTIFY	},
230638032Speter	{ "QRELAYED",		QRELAYED	},
230738032Speter	{ "QEXPANDED",		QEXPANDED	},
230838032Speter	{ "QDELIVERED",		QDELIVERED	},
230938032Speter	{ "QDELAYED",		QDELAYED	},
231038032Speter	{ "QTHISPASS",		QTHISPASS	},
231138032Speter	{ "QRCPTOK",		QRCPTOK		},
231271345Sgshapiro	{ NULL,			0		}
231338032Speter};
231438032Speter
231538032Spetervoid
2316132943Sgshapiroprintaddr(fp, a, follow)
2317132943Sgshapiro	SM_FILE_T *fp;
231838032Speter	register ADDRESS *a;
231938032Speter	bool follow;
232038032Speter{
232138032Speter	register MAILER *m;
232238032Speter	MAILER pseudomailer;
232338032Speter	register struct qflags *qfp;
232438032Speter	bool firstone;
232538032Speter
232638032Speter	if (a == NULL)
232738032Speter	{
2328132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n");
232938032Speter		return;
233038032Speter	}
233138032Speter
233238032Speter	while (a != NULL)
233338032Speter	{
2334132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a);
2335132943Sgshapiro		(void) sm_io_flush(fp, SM_TIME_DEFAULT);
233638032Speter
233738032Speter		/* find the mailer -- carefully */
233838032Speter		m = a->q_mailer;
233938032Speter		if (m == NULL)
234038032Speter		{
234138032Speter			m = &pseudomailer;
234238032Speter			m->m_mno = -1;
234338032Speter			m->m_name = "NULL";
234438032Speter		}
234538032Speter
2346132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
234790792Sgshapiro				     "%s:\n\tmailer %d (%s), host `%s'\n",
234890792Sgshapiro				     a->q_paddr == NULL ? "<null>" : a->q_paddr,
234990792Sgshapiro				     m->m_mno, m->m_name,
235090792Sgshapiro				     a->q_host == NULL ? "<null>" : a->q_host);
2351132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
235290792Sgshapiro				     "\tuser `%s', ruser `%s'\n",
235390792Sgshapiro				     a->q_user,
235490792Sgshapiro				     a->q_ruser == NULL ? "<null>" : a->q_ruser);
2355132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate=");
235664562Sgshapiro		switch (a->q_state)
235764562Sgshapiro		{
235864562Sgshapiro		  case QS_OK:
2359132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK");
236064562Sgshapiro			break;
236164562Sgshapiro
236264562Sgshapiro		  case QS_DONTSEND:
2363132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
236490792Sgshapiro					     "DONTSEND");
236564562Sgshapiro			break;
236664562Sgshapiro
236764562Sgshapiro		  case QS_BADADDR:
2368132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
236990792Sgshapiro					     "BADADDR");
237064562Sgshapiro			break;
237164562Sgshapiro
237264562Sgshapiro		  case QS_QUEUEUP:
2373132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
237490792Sgshapiro					     "QUEUEUP");
237564562Sgshapiro			break;
237664562Sgshapiro
237790792Sgshapiro		  case QS_RETRY:
2378132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY");
237990792Sgshapiro			break;
238090792Sgshapiro
238164562Sgshapiro		  case QS_SENT:
2382132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT");
238364562Sgshapiro			break;
238464562Sgshapiro
238564562Sgshapiro		  case QS_VERIFIED:
2386132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
238790792Sgshapiro					     "VERIFIED");
238864562Sgshapiro			break;
238964562Sgshapiro
239064562Sgshapiro		  case QS_EXPANDED:
2391132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
239290792Sgshapiro					     "EXPANDED");
239364562Sgshapiro			break;
239464562Sgshapiro
239564562Sgshapiro		  case QS_SENDER:
2396132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
239790792Sgshapiro					     "SENDER");
239864562Sgshapiro			break;
239964562Sgshapiro
240064562Sgshapiro		  case QS_CLONED:
2401132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
240290792Sgshapiro					     "CLONED");
240364562Sgshapiro			break;
240464562Sgshapiro
240564562Sgshapiro		  case QS_DISCARDED:
2406132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
240790792Sgshapiro					     "DISCARDED");
240864562Sgshapiro			break;
240964562Sgshapiro
241064562Sgshapiro		  case QS_REPLACED:
2411132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
241290792Sgshapiro					     "REPLACED");
241364562Sgshapiro			break;
241464562Sgshapiro
241564562Sgshapiro		  case QS_REMOVED:
2416132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
241790792Sgshapiro					     "REMOVED");
241864562Sgshapiro			break;
241964562Sgshapiro
242064562Sgshapiro		  case QS_DUPLICATE:
2421132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
242290792Sgshapiro					     "DUPLICATE");
242364562Sgshapiro			break;
242464562Sgshapiro
242564562Sgshapiro		  case QS_INCLUDED:
2426132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
242790792Sgshapiro					     "INCLUDED");
242864562Sgshapiro			break;
242964562Sgshapiro
243064562Sgshapiro		  default:
2431132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
243290792Sgshapiro					     "%d", a->q_state);
243364562Sgshapiro			break;
243464562Sgshapiro		}
2435132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
243690792Sgshapiro				     ", next=%p, alias %p, uid %d, gid %d\n",
243790792Sgshapiro				     a->q_next, a->q_alias,
243890792Sgshapiro				     (int) a->q_uid, (int) a->q_gid);
2439132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<",
244090792Sgshapiro				     a->q_flags);
244190792Sgshapiro		firstone = true;
244238032Speter		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
244338032Speter		{
244438032Speter			if (!bitset(qfp->qf_bit, a->q_flags))
244538032Speter				continue;
244638032Speter			if (!firstone)
2447132943Sgshapiro				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
244890792Sgshapiro						     ",");
244990792Sgshapiro			firstone = false;
2450132943Sgshapiro			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
245190792Sgshapiro					     qfp->qf_name);
245238032Speter		}
2453132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n");
2454132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
245590792Sgshapiro				     "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
245690792Sgshapiro				     a->q_owner == NULL ? "(none)" : a->q_owner,
245790792Sgshapiro				     a->q_home == NULL ? "(none)" : a->q_home,
245890792Sgshapiro				     a->q_fullname == NULL ? "(none)" : a->q_fullname);
2459132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
246090792Sgshapiro				     "\torcpt=\"%s\", statmta=%s, status=%s\n",
246190792Sgshapiro				     a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
246290792Sgshapiro				     a->q_statmta == NULL ? "(none)" : a->q_statmta,
246390792Sgshapiro				     a->q_status == NULL ? "(none)" : a->q_status);
2464132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
246590792Sgshapiro				     "\tfinalrcpt=\"%s\"\n",
246690792Sgshapiro				     a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
2467132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
246890792Sgshapiro				     "\trstatus=\"%s\"\n",
246990792Sgshapiro				     a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2470132943Sgshapiro		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
247190792Sgshapiro				     "\tstatdate=%s\n",
247290792Sgshapiro				     a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
247338032Speter
247438032Speter		if (!follow)
247538032Speter			return;
247638032Speter		a = a->q_next;
247738032Speter	}
247838032Speter}
247990792Sgshapiro/*
248090792Sgshapiro**  EMPTYADDR -- return true if this address is empty (``<>'')
248138032Speter**
248238032Speter**	Parameters:
248338032Speter**		a -- pointer to the address
248438032Speter**
248538032Speter**	Returns:
248690792Sgshapiro**		true -- if this address is "empty" (i.e., no one should
248738032Speter**			ever generate replies to it.
248890792Sgshapiro**		false -- if it is a "regular" (read: replyable) address.
248938032Speter*/
249038032Speter
249138032Speterbool
249238032Speteremptyaddr(a)
249338032Speter	register ADDRESS *a;
249438032Speter{
249538032Speter	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
249638032Speter	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
249738032Speter}
249890792Sgshapiro/*
249938032Speter**  REMOTENAME -- return the name relative to the current mailer
250038032Speter**
250138032Speter**	Parameters:
250238032Speter**		name -- the name to translate.
2503120256Sgshapiro**		m -- the mailer that we want to do rewriting relative to.
250438032Speter**		flags -- fine tune operations.
250538032Speter**		pstat -- pointer to status word.
250638032Speter**		e -- the current envelope.
250738032Speter**
250838032Speter**	Returns:
250938032Speter**		the text string representing this address relative to
251038032Speter**			the receiving mailer.
251138032Speter**
251238032Speter**	Side Effects:
251338032Speter**		none.
251438032Speter**
251538032Speter**	Warnings:
251638032Speter**		The text string returned is tucked away locally;
251738032Speter**			copy it if you intend to save it.
251838032Speter*/
251938032Speter
252038032Speterchar *
252138032Speterremotename(name, m, flags, pstat, e)
252238032Speter	char *name;
252338032Speter	struct mailer *m;
252438032Speter	int flags;
252538032Speter	int *pstat;
252638032Speter	register ENVELOPE *e;
252738032Speter{
252838032Speter	register char **pvp;
252990792Sgshapiro	char *SM_NONVOLATILE fancy;
253090792Sgshapiro	char *oldg;
253138032Speter	int rwset;
253238032Speter	static char buf[MAXNAME + 1];
253338032Speter	char lbuf[MAXNAME + 1];
253438032Speter	char pvpbuf[PSBUFSIZE];
253564562Sgshapiro	char addrtype[4];
253638032Speter
253738032Speter	if (tTd(12, 1))
253890792Sgshapiro		sm_dprintf("remotename(%s)\n", name);
253938032Speter
254038032Speter	/* don't do anything if we are tagging it as special */
254138032Speter	if (bitset(RF_SENDERADDR, flags))
254264562Sgshapiro	{
254338032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
254438032Speter						     : m->m_se_rwset;
254564562Sgshapiro		addrtype[2] = 's';
254664562Sgshapiro	}
254738032Speter	else
254864562Sgshapiro	{
254938032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
255038032Speter						     : m->m_re_rwset;
255164562Sgshapiro		addrtype[2] = 'r';
255264562Sgshapiro	}
255338032Speter	if (rwset < 0)
255464562Sgshapiro		return name;
255564562Sgshapiro	addrtype[1] = ' ';
255664562Sgshapiro	addrtype[3] = '\0';
255764562Sgshapiro	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
255890792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
255938032Speter
256038032Speter	/*
256138032Speter	**  Do a heuristic crack of this name to extract any comment info.
256238032Speter	**	This will leave the name as a comment and a $g macro.
256338032Speter	*/
256438032Speter
256538032Speter	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
256638032Speter		fancy = "\201g";
256738032Speter	else
2568111823Sgshapiro		fancy = crackaddr(name, e);
256938032Speter
257038032Speter	/*
257138032Speter	**  Turn the name into canonical form.
257238032Speter	**	Normally this will be RFC 822 style, i.e., "user@domain".
257338032Speter	**	If this only resolves to "user", and the "C" flag is
257438032Speter	**	specified in the sending mailer, then the sender's
257538032Speter	**	domain will be appended.
257638032Speter	*/
257738032Speter
2578132943Sgshapiro	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL, false);
257938032Speter	if (pvp == NULL)
258064562Sgshapiro		return name;
258190792Sgshapiro	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
258238032Speter		*pstat = EX_TEMPFAIL;
258338032Speter	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
258438032Speter	{
258538032Speter		/* append from domain to this address */
258638032Speter		register char **pxp = pvp;
258764562Sgshapiro		int l = MAXATOM;	/* size of buffer for pvp */
258838032Speter
258938032Speter		/* see if there is an "@domain" in the current name */
259038032Speter		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
259164562Sgshapiro		{
259238032Speter			pxp++;
259364562Sgshapiro			--l;
259464562Sgshapiro		}
259538032Speter		if (*pxp == NULL)
259638032Speter		{
259738032Speter			/* no.... append the "@domain" from the sender */
259838032Speter			register char **qxq = e->e_fromdomain;
259938032Speter
260038032Speter			while ((*pxp++ = *qxq++) != NULL)
260164562Sgshapiro			{
260264562Sgshapiro				if (--l <= 0)
260364562Sgshapiro				{
260464562Sgshapiro					*--pxp = NULL;
260564562Sgshapiro					usrerr("553 5.1.0 remotename: too many tokens");
260664562Sgshapiro					*pstat = EX_UNAVAILABLE;
260764562Sgshapiro					break;
260864562Sgshapiro				}
260964562Sgshapiro			}
261090792Sgshapiro			if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
261138032Speter				*pstat = EX_TEMPFAIL;
261238032Speter		}
261338032Speter	}
261438032Speter
261538032Speter	/*
261638032Speter	**  Do more specific rewriting.
261738032Speter	**	Rewrite using ruleset 1 or 2 depending on whether this is
261838032Speter	**		a sender address or not.
261938032Speter	**	Then run it through any receiving-mailer-specific rulesets.
262038032Speter	*/
262138032Speter
262238032Speter	if (bitset(RF_SENDERADDR, flags))
262338032Speter	{
262490792Sgshapiro		if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
262538032Speter			*pstat = EX_TEMPFAIL;
262638032Speter	}
262738032Speter	else
262838032Speter	{
262990792Sgshapiro		if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
263038032Speter			*pstat = EX_TEMPFAIL;
263138032Speter	}
263238032Speter	if (rwset > 0)
263338032Speter	{
263490792Sgshapiro		if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
263538032Speter			*pstat = EX_TEMPFAIL;
263638032Speter	}
263738032Speter
263838032Speter	/*
263938032Speter	**  Do any final sanitation the address may require.
264038032Speter	**	This will normally be used to turn internal forms
264138032Speter	**	(e.g., user@host.LOCAL) into external form.  This
264238032Speter	**	may be used as a default to the above rules.
264338032Speter	*/
264438032Speter
264590792Sgshapiro	if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
264638032Speter		*pstat = EX_TEMPFAIL;
264738032Speter
264838032Speter	/*
264938032Speter	**  Now restore the comment information we had at the beginning.
265038032Speter	*/
265138032Speter
265238032Speter	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
265390792Sgshapiro	oldg = macget(&e->e_macro, 'g');
265490792Sgshapiro	macset(&e->e_macro, 'g', lbuf);
265538032Speter
265690792Sgshapiro	SM_TRY
265790792Sgshapiro		/* need to make sure route-addrs have <angle brackets> */
265890792Sgshapiro		if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
265990792Sgshapiro			expand("<\201g>", buf, sizeof buf, e);
266090792Sgshapiro		else
266190792Sgshapiro			expand(fancy, buf, sizeof buf, e);
266290792Sgshapiro	SM_FINALLY
266390792Sgshapiro		macset(&e->e_macro, 'g', oldg);
266490792Sgshapiro	SM_END_TRY
266538032Speter
266638032Speter	if (tTd(12, 1))
266790792Sgshapiro		sm_dprintf("remotename => `%s'\n", buf);
266864562Sgshapiro	return buf;
266938032Speter}
267090792Sgshapiro/*
267138032Speter**  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
267238032Speter**
267338032Speter**	Parameters:
267438032Speter**		a -- the address to map (but just the user name part).
267538032Speter**		sendq -- the sendq in which to install any replacement
267638032Speter**			addresses.
267738032Speter**		aliaslevel -- the alias nesting depth.
267838032Speter**		e -- the envelope.
267938032Speter**
268038032Speter**	Returns:
268138032Speter**		none.
268238032Speter*/
268338032Speter
268438032Speter#define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
268538032Speter			 Q_PINGFLAGS|QHASNOTIFY|\
268690792Sgshapiro			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
268790792Sgshapiro			 QBYTRACE|QBYNDELAY|QBYNRELAY)
268838032Speter
268938032Spetervoid
269038032Spetermaplocaluser(a, sendq, aliaslevel, e)
269138032Speter	register ADDRESS *a;
269238032Speter	ADDRESS **sendq;
269338032Speter	int aliaslevel;
269438032Speter	ENVELOPE *e;
269538032Speter{
269638032Speter	register char **pvp;
269790792Sgshapiro	register ADDRESS *SM_NONVOLATILE a1 = NULL;
269838032Speter	char pvpbuf[PSBUFSIZE];
269938032Speter
270038032Speter	if (tTd(29, 1))
270138032Speter	{
270290792Sgshapiro		sm_dprintf("maplocaluser: ");
2703132943Sgshapiro		printaddr(sm_debug_file(), a, false);
270438032Speter	}
2705132943Sgshapiro	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL, false);
270638032Speter	if (pvp == NULL)
270738032Speter	{
270838032Speter		if (tTd(29, 9))
270990792Sgshapiro			sm_dprintf("maplocaluser: cannot prescan %s\n",
271064562Sgshapiro				a->q_user);
271138032Speter		return;
271238032Speter	}
271338032Speter
271490792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
271590792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
271690792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
271764562Sgshapiro
271890792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
271990792Sgshapiro	if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
272038032Speter	{
272138032Speter		if (tTd(29, 9))
272290792Sgshapiro			sm_dprintf("maplocaluser: rewrite tempfail\n");
272364562Sgshapiro		a->q_state = QS_QUEUEUP;
272438032Speter		a->q_status = "4.4.3";
272538032Speter		return;
272638032Speter	}
272738032Speter	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
272838032Speter	{
272938032Speter		if (tTd(29, 9))
273090792Sgshapiro			sm_dprintf("maplocaluser: doesn't resolve\n");
273138032Speter		return;
273238032Speter	}
273338032Speter
273490792Sgshapiro	SM_TRY
273590792Sgshapiro		a1 = buildaddr(pvp, NULL, 0, e);
273690792Sgshapiro	SM_EXCEPT(exc, "E:mta.quickabort")
273790792Sgshapiro
273890792Sgshapiro		/*
273990792Sgshapiro		**  mark address as bad, S5 returned an error
274090792Sgshapiro		**	and we gave that back to the SMTP client.
274190792Sgshapiro		*/
274290792Sgshapiro
274390792Sgshapiro		a->q_state = QS_DONTSEND;
274490792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
274590792Sgshapiro	SM_END_TRY
274690792Sgshapiro
274738032Speter	/* if non-null, mailer destination specified -- has it changed? */
274838032Speter	if (a1 == NULL || sameaddr(a, a1))
274938032Speter	{
275038032Speter		if (tTd(29, 9))
275190792Sgshapiro			sm_dprintf("maplocaluser: address unchanged\n");
275238032Speter		return;
275338032Speter	}
275438032Speter
275538032Speter	/* make new address take on flags and print attributes of old */
275638032Speter	a1->q_flags &= ~Q_COPYFLAGS;
275738032Speter	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
275890792Sgshapiro	a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
275990792Sgshapiro	a1->q_finalrcpt = a->q_finalrcpt;
276064562Sgshapiro	a1->q_orcpt = a->q_orcpt;
276138032Speter
276238032Speter	/* mark old address as dead; insert new address */
276364562Sgshapiro	a->q_state = QS_REPLACED;
276438032Speter	if (tTd(29, 5))
276538032Speter	{
276690792Sgshapiro		sm_dprintf("maplocaluser: QS_REPLACED ");
2767132943Sgshapiro		printaddr(sm_debug_file(), a, false);
276838032Speter	}
276938032Speter	a1->q_alias = a;
277090792Sgshapiro	allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
277138032Speter	(void) recipient(a1, sendq, aliaslevel, e);
277238032Speter}
277390792Sgshapiro/*
277438032Speter**  DEQUOTE_INIT -- initialize dequote map
277538032Speter**
277638032Speter**	Parameters:
277738032Speter**		map -- the internal map structure.
277838032Speter**		args -- arguments.
277938032Speter**
278038032Speter**	Returns:
278190792Sgshapiro**		true.
278238032Speter*/
278338032Speter
278438032Speterbool
278538032Speterdequote_init(map, args)
278638032Speter	MAP *map;
278738032Speter	char *args;
278838032Speter{
278938032Speter	register char *p = args;
279038032Speter
279164562Sgshapiro	/* there is no check whether there is really an argument */
279238032Speter	map->map_mflags |= MF_KEEPQUOTES;
279338032Speter	for (;;)
279438032Speter	{
279538032Speter		while (isascii(*p) && isspace(*p))
279638032Speter			p++;
279738032Speter		if (*p != '-')
279838032Speter			break;
279938032Speter		switch (*++p)
280038032Speter		{
280138032Speter		  case 'a':
280238032Speter			map->map_app = ++p;
280338032Speter			break;
280438032Speter
280564562Sgshapiro		  case 'D':
280664562Sgshapiro			map->map_mflags |= MF_DEFER;
280764562Sgshapiro			break;
280864562Sgshapiro
280964562Sgshapiro		  case 'S':
281038032Speter		  case 's':
281164562Sgshapiro			map->map_spacesub = *++p;
281238032Speter			break;
281338032Speter		}
281438032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
281538032Speter			p++;
281638032Speter		if (*p != '\0')
281738032Speter			*p = '\0';
281838032Speter	}
281938032Speter	if (map->map_app != NULL)
282038032Speter		map->map_app = newstr(map->map_app);
282138032Speter
282290792Sgshapiro	return true;
282338032Speter}
282490792Sgshapiro/*
282538032Speter**  DEQUOTE_MAP -- unquote an address
282638032Speter**
282738032Speter**	Parameters:
282838032Speter**		map -- the internal map structure (ignored).
282938032Speter**		name -- the name to dequote.
283038032Speter**		av -- arguments (ignored).
283138032Speter**		statp -- pointer to status out-parameter.
283238032Speter**
283338032Speter**	Returns:
283438032Speter**		NULL -- if there were no quotes, or if the resulting
283538032Speter**			unquoted buffer would not be acceptable to prescan.
283638032Speter**		else -- The dequoted buffer.
283738032Speter*/
283838032Speter
283938032Speter/* ARGSUSED2 */
284038032Speterchar *
284138032Speterdequote_map(map, name, av, statp)
284238032Speter	MAP *map;
284338032Speter	char *name;
284438032Speter	char **av;
284538032Speter	int *statp;
284638032Speter{
284738032Speter	register char *p;
284838032Speter	register char *q;
284938032Speter	register char c;
285038032Speter	int anglecnt = 0;
285138032Speter	int cmntcnt = 0;
285238032Speter	int quotecnt = 0;
285338032Speter	int spacecnt = 0;
285490792Sgshapiro	bool quotemode = false;
285590792Sgshapiro	bool bslashmode = false;
285664562Sgshapiro	char spacesub = map->map_spacesub;
285738032Speter
285838032Speter	for (p = q = name; (c = *p++) != '\0'; )
285938032Speter	{
286038032Speter		if (bslashmode)
286138032Speter		{
286290792Sgshapiro			bslashmode = false;
286338032Speter			*q++ = c;
286438032Speter			continue;
286538032Speter		}
286638032Speter
286738032Speter		if (c == ' ' && spacesub != '\0')
286838032Speter			c = spacesub;
286938032Speter
287038032Speter		switch (c)
287138032Speter		{
287238032Speter		  case '\\':
287390792Sgshapiro			bslashmode = true;
287438032Speter			break;
287538032Speter
287638032Speter		  case '(':
287738032Speter			cmntcnt++;
287838032Speter			break;
287938032Speter
288038032Speter		  case ')':
288138032Speter			if (cmntcnt-- <= 0)
288238032Speter				return NULL;
288338032Speter			break;
288438032Speter
288538032Speter		  case ' ':
288664562Sgshapiro		  case '\t':
288738032Speter			spacecnt++;
288838032Speter			break;
288938032Speter		}
289038032Speter
289138032Speter		if (cmntcnt > 0)
289238032Speter		{
289338032Speter			*q++ = c;
289438032Speter			continue;
289538032Speter		}
289638032Speter
289738032Speter		switch (c)
289838032Speter		{
289938032Speter		  case '"':
290038032Speter			quotemode = !quotemode;
290138032Speter			quotecnt++;
290238032Speter			continue;
290338032Speter
290438032Speter		  case '<':
290538032Speter			anglecnt++;
290638032Speter			break;
290738032Speter
290838032Speter		  case '>':
290938032Speter			if (anglecnt-- <= 0)
291038032Speter				return NULL;
291138032Speter			break;
291238032Speter		}
291338032Speter		*q++ = c;
291438032Speter	}
291538032Speter
291638032Speter	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
291738032Speter	    quotemode || quotecnt <= 0 || spacecnt != 0)
291838032Speter		return NULL;
291938032Speter	*q++ = '\0';
292038032Speter	return map_rewrite(map, name, strlen(name), NULL);
292138032Speter}
292290792Sgshapiro/*
292338032Speter**  RSCHECK -- check string(s) for validity using rewriting sets
292438032Speter**
292538032Speter**	Parameters:
292638032Speter**		rwset -- the rewriting set to use.
292738032Speter**		p1 -- the first string to check.
292838032Speter**		p2 -- the second string to check -- may be null.
292938032Speter**		e -- the current envelope.
2930102528Sgshapiro**		flags -- control some behavior, see RSF_ in sendmail.h
293190792Sgshapiro**		logl -- logging level.
293271345Sgshapiro**		host -- NULL or relay host.
293390792Sgshapiro**		logid -- id for sm_syslog.
293438032Speter**
293538032Speter**	Returns:
293638032Speter**		EX_OK -- if the rwset doesn't resolve to $#error
293738032Speter**		else -- the failure status (message printed)
293838032Speter*/
293938032Speter
294038032Speterint
2941102528Sgshapirorscheck(rwset, p1, p2, e, flags, logl, host, logid)
294238032Speter	char *rwset;
294338032Speter	char *p1;
294438032Speter	char *p2;
294538032Speter	ENVELOPE *e;
2946102528Sgshapiro	int flags;
294764562Sgshapiro	int logl;
294871345Sgshapiro	char *host;
294990792Sgshapiro	char *logid;
295038032Speter{
295190792Sgshapiro	char *volatile buf;
295238032Speter	int bufsize;
295338032Speter	int saveexitstat;
295490792Sgshapiro	int volatile rstat = EX_OK;
295538032Speter	char **pvp;
295638032Speter	int rsno;
295790792Sgshapiro	bool volatile discard = false;
295838032Speter	auto ADDRESS a1;
295938032Speter	bool saveQuickAbort = QuickAbort;
296038032Speter	bool saveSuprErrs = SuprErrs;
296190792Sgshapiro	bool quarantine = false;
296290792Sgshapiro	char ubuf[BUFSIZ * 2];
296338032Speter	char buf0[MAXLINE];
296438032Speter	char pvpbuf[PSBUFSIZE];
296538032Speter	extern char MsgBuf[];
296638032Speter
296738032Speter	if (tTd(48, 2))
296890792Sgshapiro		sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
296938032Speter			p2 == NULL ? "(NULL)" : p2);
297038032Speter
297138032Speter	rsno = strtorwset(rwset, NULL, ST_FIND);
297238032Speter	if (rsno < 0)
297338032Speter		return EX_OK;
297438032Speter
297538032Speter	if (p2 != NULL)
297638032Speter	{
297738032Speter		bufsize = strlen(p1) + strlen(p2) + 2;
297838032Speter		if (bufsize > sizeof buf0)
297990792Sgshapiro			buf = sm_malloc_x(bufsize);
298038032Speter		else
298138032Speter		{
298238032Speter			buf = buf0;
298338032Speter			bufsize = sizeof buf0;
298438032Speter		}
298590792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
298638032Speter	}
298738032Speter	else
298838032Speter	{
298938032Speter		bufsize = strlen(p1) + 1;
299038032Speter		if (bufsize > sizeof buf0)
299190792Sgshapiro			buf = sm_malloc_x(bufsize);
299238032Speter		else
299338032Speter		{
299438032Speter			buf = buf0;
299538032Speter			bufsize = sizeof buf0;
299638032Speter		}
299790792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
299838032Speter	}
299990792Sgshapiro	SM_TRY
300038032Speter	{
300190792Sgshapiro		SuprErrs = true;
300290792Sgshapiro		QuickAbort = false;
300390792Sgshapiro		pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
3004132943Sgshapiro			      bitset(RSF_RMCOMM, flags) ? NULL : TokTypeNoC,
3005132943Sgshapiro			      bitset(RSF_RMCOMM, flags) ? false : true);
300690792Sgshapiro		SuprErrs = saveSuprErrs;
300790792Sgshapiro		if (pvp == NULL)
300890792Sgshapiro		{
300990792Sgshapiro			if (tTd(48, 2))
301090792Sgshapiro				sm_dprintf("rscheck: cannot prescan input\n");
301190792Sgshapiro	/*
301290792Sgshapiro			syserr("rscheck: cannot prescan input: \"%s\"",
301390792Sgshapiro				shortenstring(buf, MAXSHORTSTR));
301490792Sgshapiro			rstat = EX_DATAERR;
301590792Sgshapiro	*/
301690792Sgshapiro			goto finis;
301790792Sgshapiro		}
3018102528Sgshapiro		if (bitset(RSF_UNSTRUCTURED, flags))
3019102528Sgshapiro			SuprErrs = true;
302090792Sgshapiro		(void) REWRITE(pvp, rsno, e);
3021102528Sgshapiro		if (bitset(RSF_UNSTRUCTURED, flags))
3022102528Sgshapiro			SuprErrs = saveSuprErrs;
302390792Sgshapiro		if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
302490792Sgshapiro		    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
302590792Sgshapiro				       strcmp(pvp[1], "discard") != 0))
302690792Sgshapiro		{
302790792Sgshapiro			goto finis;
302890792Sgshapiro		}
302966494Sgshapiro
303090792Sgshapiro		if (strcmp(pvp[1], "discard") == 0)
303190792Sgshapiro		{
303290792Sgshapiro			if (tTd(48, 2))
303390792Sgshapiro				sm_dprintf("rscheck: discard mailer selected\n");
303490792Sgshapiro			e->e_flags |= EF_DISCARD;
303590792Sgshapiro			discard = true;
303690792Sgshapiro		}
303790792Sgshapiro		else if (strcmp(pvp[1], "error") == 0 &&
303890792Sgshapiro			 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
303990792Sgshapiro			 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
304090792Sgshapiro		{
304190792Sgshapiro			if (pvp[4] == NULL ||
304290792Sgshapiro			    (pvp[4][0] & 0377) != CANONUSER ||
304390792Sgshapiro			    pvp[5] == NULL)
304490792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
304590792Sgshapiro								 rwset);
304690792Sgshapiro			else
304790792Sgshapiro			{
304890792Sgshapiro				cataddr(&(pvp[5]), NULL, ubuf,
304990792Sgshapiro					sizeof ubuf, ' ');
305090792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
305190792Sgshapiro								 ubuf);
305290792Sgshapiro			}
305390792Sgshapiro			macdefine(&e->e_macro, A_PERM,
305490792Sgshapiro				  macid("{quarantine}"), e->e_quarmsg);
305590792Sgshapiro			quarantine = true;
305690792Sgshapiro		}
305790792Sgshapiro		else
305890792Sgshapiro		{
305990792Sgshapiro			int savelogusrerrs = LogUsrErrs;
306090792Sgshapiro			static bool logged = false;
306190792Sgshapiro
306290792Sgshapiro			/* got an error -- process it */
306390792Sgshapiro			saveexitstat = ExitStat;
306490792Sgshapiro			LogUsrErrs = false;
306590792Sgshapiro			(void) buildaddr(pvp, &a1, 0, e);
306690792Sgshapiro			LogUsrErrs = savelogusrerrs;
306790792Sgshapiro			rstat = ExitStat;
306890792Sgshapiro			ExitStat = saveexitstat;
306990792Sgshapiro			if (!logged)
307090792Sgshapiro			{
3071102528Sgshapiro				if (bitset(RSF_COUNT, flags))
307290792Sgshapiro					markstats(e, &a1, STATS_REJECT);
307390792Sgshapiro				logged = true;
307490792Sgshapiro			}
307590792Sgshapiro		}
307690792Sgshapiro
307790792Sgshapiro		if (LogLevel > logl)
307890792Sgshapiro		{
307990792Sgshapiro			char *relay;
308090792Sgshapiro			char *p;
308190792Sgshapiro			char lbuf[MAXLINE];
308290792Sgshapiro
308390792Sgshapiro			p = lbuf;
308490792Sgshapiro			if (p2 != NULL)
308590792Sgshapiro			{
308690792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
308790792Sgshapiro					", arg2=%s",
308890792Sgshapiro					p2);
308990792Sgshapiro				p += strlen(p);
309090792Sgshapiro			}
309190792Sgshapiro
309290792Sgshapiro			if (host != NULL)
309390792Sgshapiro				relay = host;
309490792Sgshapiro			else
309590792Sgshapiro				relay = macvalue('_', e);
309690792Sgshapiro			if (relay != NULL)
309790792Sgshapiro			{
309890792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
309990792Sgshapiro					", relay=%s", relay);
310090792Sgshapiro				p += strlen(p);
310190792Sgshapiro			}
310290792Sgshapiro			*p = '\0';
310390792Sgshapiro			if (discard)
310490792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
310590792Sgshapiro					  "ruleset=%s, arg1=%s%s, discard",
310690792Sgshapiro					  rwset, p1, lbuf);
310790792Sgshapiro			else if (quarantine)
310890792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
310990792Sgshapiro					  "ruleset=%s, arg1=%s%s, quarantine=%s",
311090792Sgshapiro					  rwset, p1, lbuf, ubuf);
311190792Sgshapiro			else
311290792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
311390792Sgshapiro					  "ruleset=%s, arg1=%s%s, reject=%s",
311490792Sgshapiro					  rwset, p1, lbuf, MsgBuf);
311590792Sgshapiro		}
311690792Sgshapiro
311790792Sgshapiro	 finis: ;
311873188Sgshapiro	}
311990792Sgshapiro	SM_FINALLY
312038032Speter	{
312190792Sgshapiro		/* clean up */
312290792Sgshapiro		if (buf != buf0)
312390792Sgshapiro			sm_free(buf);
312490792Sgshapiro		QuickAbort = saveQuickAbort;
312538032Speter	}
312690792Sgshapiro	SM_END_TRY
312738032Speter
312890792Sgshapiro	setstat(rstat);
312990792Sgshapiro
313090792Sgshapiro	/* rulesets don't set errno */
313190792Sgshapiro	errno = 0;
313290792Sgshapiro	if (rstat != EX_OK && QuickAbort)
313390792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
313490792Sgshapiro	return rstat;
313590792Sgshapiro}
313690792Sgshapiro/*
313790792Sgshapiro**  RSCAP -- call rewriting set to return capabilities
313890792Sgshapiro**
313990792Sgshapiro**	Parameters:
314090792Sgshapiro**		rwset -- the rewriting set to use.
314190792Sgshapiro**		p1 -- the first string to check.
314290792Sgshapiro**		p2 -- the second string to check -- may be null.
314390792Sgshapiro**		e -- the current envelope.
314490792Sgshapiro**		pvp -- pointer to token vector.
314590792Sgshapiro**		pvpbuf -- buffer space.
3146120256Sgshapiro**		size -- size of buffer space.
314790792Sgshapiro**
314890792Sgshapiro**	Returns:
314990792Sgshapiro**		EX_UNAVAILABLE -- ruleset doesn't exist.
315090792Sgshapiro**		EX_DATAERR -- prescan() failed.
315190792Sgshapiro**		EX_OK -- rewrite() was successful.
315290792Sgshapiro**		else -- return status from rewrite().
315390792Sgshapiro*/
315490792Sgshapiro
315590792Sgshapiroint
315690792Sgshapirorscap(rwset, p1, p2, e, pvp, pvpbuf, size)
315790792Sgshapiro	char *rwset;
315890792Sgshapiro	char *p1;
315990792Sgshapiro	char *p2;
316090792Sgshapiro	ENVELOPE *e;
316190792Sgshapiro	char ***pvp;
316290792Sgshapiro	char *pvpbuf;
316390792Sgshapiro	int size;
316490792Sgshapiro{
316590792Sgshapiro	char *volatile buf;
316690792Sgshapiro	int bufsize;
316790792Sgshapiro	int volatile rstat = EX_OK;
316890792Sgshapiro	int rsno;
316990792Sgshapiro	bool saveQuickAbort = QuickAbort;
317090792Sgshapiro	bool saveSuprErrs = SuprErrs;
317190792Sgshapiro	char buf0[MAXLINE];
317290792Sgshapiro	extern char MsgBuf[];
317390792Sgshapiro
317490792Sgshapiro	if (tTd(48, 2))
317590792Sgshapiro		sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
317690792Sgshapiro			p2 == NULL ? "(NULL)" : p2);
317790792Sgshapiro
317890792Sgshapiro	if (pvp != NULL)
317990792Sgshapiro		*pvp = NULL;
318090792Sgshapiro	rsno = strtorwset(rwset, NULL, ST_FIND);
318190792Sgshapiro	if (rsno < 0)
318290792Sgshapiro		return EX_UNAVAILABLE;
318390792Sgshapiro
318490792Sgshapiro	if (p2 != NULL)
318538032Speter	{
318690792Sgshapiro		bufsize = strlen(p1) + strlen(p2) + 2;
318790792Sgshapiro		if (bufsize > sizeof buf0)
318890792Sgshapiro			buf = sm_malloc_x(bufsize);
318990792Sgshapiro		else
319090792Sgshapiro		{
319190792Sgshapiro			buf = buf0;
319290792Sgshapiro			bufsize = sizeof buf0;
319390792Sgshapiro		}
319490792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
319538032Speter	}
319664562Sgshapiro	else
319738032Speter	{
319890792Sgshapiro		bufsize = strlen(p1) + 1;
319990792Sgshapiro		if (bufsize > sizeof buf0)
320090792Sgshapiro			buf = sm_malloc_x(bufsize);
320190792Sgshapiro		else
320238032Speter		{
320390792Sgshapiro			buf = buf0;
320490792Sgshapiro			bufsize = sizeof buf0;
320538032Speter		}
320690792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
320738032Speter	}
320890792Sgshapiro	SM_TRY
320938032Speter	{
321090792Sgshapiro		SuprErrs = true;
321190792Sgshapiro		QuickAbort = false;
3212132943Sgshapiro		*pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL, false);
321390792Sgshapiro		if (*pvp != NULL)
3214120256Sgshapiro			rstat = rewrite(*pvp, rsno, 0, e, size);
321571345Sgshapiro		else
321638032Speter		{
321790792Sgshapiro			if (tTd(48, 2))
321890792Sgshapiro				sm_dprintf("rscap: cannot prescan input\n");
321990792Sgshapiro			rstat = EX_DATAERR;
322038032Speter		}
322138032Speter	}
322290792Sgshapiro	SM_FINALLY
322390792Sgshapiro	{
322490792Sgshapiro		/* clean up */
322590792Sgshapiro		if (buf != buf0)
322690792Sgshapiro			sm_free(buf);
322790792Sgshapiro		SuprErrs = saveSuprErrs;
322890792Sgshapiro		QuickAbort = saveQuickAbort;
322938032Speter
323090792Sgshapiro		/* prevent information leak, this may contain rewrite error */
323190792Sgshapiro		MsgBuf[0] = '\0';
323290792Sgshapiro	}
323390792Sgshapiro	SM_END_TRY
323438032Speter	return rstat;
323538032Speter}
3236