parseaddr.c revision 98841
138032Speter/*
294334Sgshapiro * Copyright (c) 1998-2002 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
1698841SgshapiroSM_RCSID("@(#)$Id: parseaddr.c,v 8.359.2.1 2002/06/19 18:24:26 gshapiro 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
9338032Speter	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
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-->");
23190792Sgshapiro		printaddr(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.
46338032Speter**
46438032Speter**	Returns:
46538032Speter**		A pointer to a vector of tokens.
46638032Speter**		NULL on error.
46738032Speter*/
46838032Speter
46938032Speter/* states and character types */
47064562Sgshapiro#define OPR		0	/* operator */
47164562Sgshapiro#define ATM		1	/* atom */
47264562Sgshapiro#define QST		2	/* in quoted string */
47364562Sgshapiro#define SPC		3	/* chewing up spaces */
47464562Sgshapiro#define ONE		4	/* pick up one character */
47564562Sgshapiro#define ILL		5	/* illegal character */
47638032Speter
47764562Sgshapiro#define NSTATES	6	/* number of states */
47864562Sgshapiro#define TYPE		017	/* mask to select state type */
47938032Speter
48038032Speter/* meta bits for table */
48164562Sgshapiro#define M		020	/* meta character; don't pass through */
48264562Sgshapiro#define B		040	/* cause a break */
48364562Sgshapiro#define MB		M|B	/* meta-break */
48438032Speter
48538032Speterstatic short StateTab[NSTATES][NSTATES] =
48638032Speter{
48738032Speter   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
48838032Speter	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
48938032Speter	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
49038032Speter	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},
49138032Speter	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},
49238032Speter	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},
49338032Speter	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},
49438032Speter};
49538032Speter
49638032Speter/* token type table -- it gets modified with $o characters */
49790792Sgshapirostatic unsigned char	TokTypeTab[256] =
49838032Speter{
49938032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
50038032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
50138032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
50238032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
50338032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
50464562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
50538032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
50638032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
50738032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
50838032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
50938032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
51038032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51138032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
51238032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51338032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
51438032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
51538032Speter
51638032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
51738032Speter	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
51838032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
51938032Speter	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
52038032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
52138032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52238032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
52338032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52438032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
52538032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52638032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
52738032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
52838032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
52938032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
53038032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
53138032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
53238032Speter};
53338032Speter
53438032Speter/* token type table for MIME parsing */
53590792Sgshapirounsigned char	MimeTokenTab[256] =
53638032Speter{
53738032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
53838032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
53938032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
54038032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
54138032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
54264562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
54338032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
54438032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
54538032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
54638032Speter	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
54738032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
54838032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
54938032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
55038032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
55138032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
55238032Speter	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
55338032Speter
55438032Speter    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
55538032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
55638032Speter    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
55738032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
55838032Speter    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
55938032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56038032Speter    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
56138032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56238032Speter    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
56338032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56438032Speter    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
56538032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56638032Speter    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
56738032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
56838032Speter    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
56938032Speter	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
57038032Speter};
57138032Speter
57264562Sgshapiro/* token type table: don't strip comments */
57390792Sgshapirounsigned char	TokTypeNoC[256] =
57464562Sgshapiro{
57564562Sgshapiro    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
57664562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
57764562Sgshapiro    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
57864562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
57964562Sgshapiro    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
58064562Sgshapiro	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
58164562Sgshapiro    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
58264562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58364562Sgshapiro    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
58464562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58564562Sgshapiro    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
58664562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58764562Sgshapiro    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
58864562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
58964562Sgshapiro    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
59064562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
59138032Speter
59264562Sgshapiro    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
59364562Sgshapiro	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
59464562Sgshapiro    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
59564562Sgshapiro	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
59664562Sgshapiro    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
59764562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
59864562Sgshapiro    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
59964562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60064562Sgshapiro    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
60164562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60264562Sgshapiro    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
60364562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60464562Sgshapiro    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
60564562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60664562Sgshapiro    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
60764562Sgshapiro	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
60864562Sgshapiro};
60938032Speter
61064562Sgshapiro
61164562Sgshapiro#define NOCHAR		-1	/* signal nothing in lookahead token */
61264562Sgshapiro
61338032Speterchar **
61438032Speterprescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
61538032Speter	char *addr;
61638032Speter	int delim;
61738032Speter	char pvpbuf[];
61838032Speter	int pvpbsize;
61938032Speter	char **delimptr;
62090792Sgshapiro	unsigned char *toktab;
62138032Speter{
62238032Speter	register char *p;
62338032Speter	register char *q;
62438032Speter	register int c;
62538032Speter	char **avp;
62638032Speter	bool bslashmode;
62738032Speter	bool route_syntax;
62838032Speter	int cmntcnt;
62938032Speter	int anglecnt;
63038032Speter	char *tok;
63138032Speter	int state;
63238032Speter	int newstate;
63338032Speter	char *saveto = CurEnv->e_to;
63464562Sgshapiro	static char *av[MAXATOM + 1];
63590792Sgshapiro	static bool firsttime = true;
63638032Speter	extern int errno;
63738032Speter
63838032Speter	if (firsttime)
63938032Speter	{
64038032Speter		/* initialize the token type table */
64138032Speter		char obuf[50];
64238032Speter
64390792Sgshapiro		firsttime = false;
64438032Speter		if (OperatorChars == NULL)
64538032Speter		{
64638032Speter			if (ConfigLevel < 7)
64738032Speter				OperatorChars = macvalue('o', CurEnv);
64838032Speter			if (OperatorChars == NULL)
64938032Speter				OperatorChars = ".:@[]";
65038032Speter		}
65164562Sgshapiro		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
65264562Sgshapiro		       CurEnv);
65390792Sgshapiro		(void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf);
65438032Speter		for (p = obuf; *p != '\0'; p++)
65538032Speter		{
65638032Speter			if (TokTypeTab[*p & 0xff] == ATM)
65738032Speter				TokTypeTab[*p & 0xff] = OPR;
65864562Sgshapiro			if (TokTypeNoC[*p & 0xff] == ATM)
65964562Sgshapiro				TokTypeNoC[*p & 0xff] = OPR;
66038032Speter		}
66138032Speter	}
66238032Speter	if (toktab == NULL)
66338032Speter		toktab = TokTypeTab;
66438032Speter
66538032Speter	/* make sure error messages don't have garbage on them */
66638032Speter	errno = 0;
66738032Speter
66838032Speter	q = pvpbuf;
66990792Sgshapiro	bslashmode = false;
67090792Sgshapiro	route_syntax = false;
67138032Speter	cmntcnt = 0;
67238032Speter	anglecnt = 0;
67338032Speter	avp = av;
67438032Speter	state = ATM;
67538032Speter	c = NOCHAR;
67638032Speter	p = addr;
67738032Speter	CurEnv->e_to = p;
67838032Speter	if (tTd(22, 11))
67938032Speter	{
68090792Sgshapiro		sm_dprintf("prescan: ");
68138032Speter		xputs(p);
68290792Sgshapiro		sm_dprintf("\n");
68338032Speter	}
68438032Speter
68538032Speter	do
68638032Speter	{
68738032Speter		/* read a token */
68838032Speter		tok = q;
68938032Speter		for (;;)
69038032Speter		{
69138032Speter			/* store away any old lookahead character */
69238032Speter			if (c != NOCHAR && !bslashmode)
69338032Speter			{
69438032Speter				/* see if there is room */
69538032Speter				if (q >= &pvpbuf[pvpbsize - 5])
69638032Speter				{
69764562Sgshapiro					usrerr("553 5.1.1 Address too long");
69890792Sgshapiro					if (strlen(addr) > MAXNAME)
69938032Speter						addr[MAXNAME] = '\0';
70038032Speter	returnnull:
70138032Speter					if (delimptr != NULL)
70238032Speter						*delimptr = p;
70338032Speter					CurEnv->e_to = saveto;
70464562Sgshapiro					return NULL;
70538032Speter				}
70638032Speter
70738032Speter				/* squirrel it away */
70838032Speter				*q++ = c;
70938032Speter			}
71038032Speter
71138032Speter			/* read a new input character */
71238032Speter			c = *p++;
71338032Speter			if (c == '\0')
71438032Speter			{
71538032Speter				/* diagnose and patch up bad syntax */
71638032Speter				if (state == QST)
71738032Speter				{
71890792Sgshapiro					usrerr("553 Unbalanced '\"'");
71938032Speter					c = '"';
72038032Speter				}
72138032Speter				else if (cmntcnt > 0)
72238032Speter				{
72390792Sgshapiro					usrerr("553 Unbalanced '('");
72438032Speter					c = ')';
72538032Speter				}
72638032Speter				else if (anglecnt > 0)
72738032Speter				{
72838032Speter					c = '>';
72990792Sgshapiro					usrerr("553 Unbalanced '<'");
73038032Speter				}
73138032Speter				else
73238032Speter					break;
73338032Speter
73438032Speter				p--;
73538032Speter			}
73638032Speter			else if (c == delim && cmntcnt <= 0 && state != QST)
73738032Speter			{
73838032Speter				if (anglecnt <= 0)
73938032Speter					break;
74038032Speter
74138032Speter				/* special case for better error management */
74238032Speter				if (delim == ',' && !route_syntax)
74338032Speter				{
74490792Sgshapiro					usrerr("553 Unbalanced '<'");
74538032Speter					c = '>';
74638032Speter					p--;
74738032Speter				}
74838032Speter			}
74938032Speter
75038032Speter			if (tTd(22, 101))
75190792Sgshapiro				sm_dprintf("c=%c, s=%d; ", c, state);
75238032Speter
75338032Speter			/* chew up special characters */
75438032Speter			*q = '\0';
75538032Speter			if (bslashmode)
75638032Speter			{
75790792Sgshapiro				bslashmode = false;
75838032Speter
75938032Speter				/* kludge \! for naive users */
76038032Speter				if (cmntcnt > 0)
76138032Speter				{
76238032Speter					c = NOCHAR;
76338032Speter					continue;
76438032Speter				}
76538032Speter				else if (c != '!' || state == QST)
76638032Speter				{
76738032Speter					*q++ = '\\';
76838032Speter					continue;
76938032Speter				}
77038032Speter			}
77138032Speter
77238032Speter			if (c == '\\')
77338032Speter			{
77490792Sgshapiro				bslashmode = true;
77538032Speter			}
77638032Speter			else if (state == QST)
77738032Speter			{
77864562Sgshapiro				/* EMPTY */
77938032Speter				/* do nothing, just avoid next clauses */
78038032Speter			}
78164562Sgshapiro			else if (c == '(' && toktab['('] == SPC)
78238032Speter			{
78338032Speter				cmntcnt++;
78438032Speter				c = NOCHAR;
78538032Speter			}
78664562Sgshapiro			else if (c == ')' && toktab['('] == SPC)
78738032Speter			{
78838032Speter				if (cmntcnt <= 0)
78938032Speter				{
79090792Sgshapiro					usrerr("553 Unbalanced ')'");
79138032Speter					c = NOCHAR;
79238032Speter				}
79338032Speter				else
79438032Speter					cmntcnt--;
79538032Speter			}
79638032Speter			else if (cmntcnt > 0)
79764562Sgshapiro			{
79838032Speter				c = NOCHAR;
79964562Sgshapiro			}
80038032Speter			else if (c == '<')
80138032Speter			{
80264562Sgshapiro				char *ptr = p;
80338032Speter
80438032Speter				anglecnt++;
80564562Sgshapiro				while (isascii(*ptr) && isspace(*ptr))
80664562Sgshapiro					ptr++;
80764562Sgshapiro				if (*ptr == '@')
80890792Sgshapiro					route_syntax = true;
80938032Speter			}
81038032Speter			else if (c == '>')
81138032Speter			{
81238032Speter				if (anglecnt <= 0)
81338032Speter				{
81490792Sgshapiro					usrerr("553 Unbalanced '>'");
81538032Speter					c = NOCHAR;
81638032Speter				}
81738032Speter				else
81838032Speter					anglecnt--;
81990792Sgshapiro				route_syntax = false;
82038032Speter			}
82138032Speter			else if (delim == ' ' && isascii(c) && isspace(c))
82238032Speter				c = ' ';
82338032Speter
82438032Speter			if (c == NOCHAR)
82538032Speter				continue;
82638032Speter
82738032Speter			/* see if this is end of input */
82838032Speter			if (c == delim && anglecnt <= 0 && state != QST)
82938032Speter				break;
83038032Speter
83138032Speter			newstate = StateTab[state][toktab[c & 0xff]];
83238032Speter			if (tTd(22, 101))
83390792Sgshapiro				sm_dprintf("ns=%02o\n", newstate);
83438032Speter			state = newstate & TYPE;
83538032Speter			if (state == ILL)
83638032Speter			{
83738032Speter				if (isascii(c) && isprint(c))
83890792Sgshapiro					usrerr("553 Illegal character %c", c);
83938032Speter				else
84090792Sgshapiro					usrerr("553 Illegal character 0x%02x",
84190792Sgshapiro					       c & 0x0ff);
84238032Speter			}
84338032Speter			if (bitset(M, newstate))
84438032Speter				c = NOCHAR;
84538032Speter			if (bitset(B, newstate))
84638032Speter				break;
84738032Speter		}
84838032Speter
84938032Speter		/* new token */
85038032Speter		if (tok != q)
85138032Speter		{
85238032Speter			*q++ = '\0';
85338032Speter			if (tTd(22, 36))
85438032Speter			{
85590792Sgshapiro				sm_dprintf("tok=");
85638032Speter				xputs(tok);
85790792Sgshapiro				sm_dprintf("\n");
85838032Speter			}
85938032Speter			if (avp >= &av[MAXATOM])
86038032Speter			{
86164562Sgshapiro				usrerr("553 5.1.0 prescan: too many tokens");
86238032Speter				goto returnnull;
86338032Speter			}
86438032Speter			if (q - tok > MAXNAME)
86538032Speter			{
86664562Sgshapiro				usrerr("553 5.1.0 prescan: token too long");
86738032Speter				goto returnnull;
86838032Speter			}
86938032Speter			*avp++ = tok;
87038032Speter		}
87138032Speter	} while (c != '\0' && (c != delim || anglecnt > 0));
87238032Speter	*avp = NULL;
87338032Speter	p--;
87438032Speter	if (delimptr != NULL)
87538032Speter		*delimptr = p;
87638032Speter	if (tTd(22, 12))
87738032Speter	{
87890792Sgshapiro		sm_dprintf("prescan==>");
87938032Speter		printav(av);
88038032Speter	}
88138032Speter	CurEnv->e_to = saveto;
88238032Speter	if (av[0] == NULL)
88338032Speter	{
88438032Speter		if (tTd(22, 1))
88590792Sgshapiro			sm_dprintf("prescan: null leading token\n");
88664562Sgshapiro		return NULL;
88738032Speter	}
88864562Sgshapiro	return av;
88938032Speter}
89090792Sgshapiro/*
89138032Speter**  REWRITE -- apply rewrite rules to token vector.
89238032Speter**
89338032Speter**	This routine is an ordered production system.  Each rewrite
89438032Speter**	rule has a LHS (called the pattern) and a RHS (called the
89538032Speter**	rewrite); 'rwr' points the the current rewrite rule.
89638032Speter**
89738032Speter**	For each rewrite rule, 'avp' points the address vector we
89838032Speter**	are trying to match against, and 'pvp' points to the pattern.
89938032Speter**	If pvp points to a special match value (MATCHZANY, MATCHANY,
90038032Speter**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
90138032Speter**	matched is saved away in the match vector (pointed to by 'mvp').
90238032Speter**
90338032Speter**	When a match between avp & pvp does not match, we try to
90438032Speter**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
90538032Speter**	we must also back out the match in mvp.  If we reach a
90638032Speter**	MATCHANY or MATCHZANY we just extend the match and start
90738032Speter**	over again.
90838032Speter**
90938032Speter**	When we finally match, we rewrite the address vector
91038032Speter**	and try over again.
91138032Speter**
91238032Speter**	Parameters:
91338032Speter**		pvp -- pointer to token vector.
91438032Speter**		ruleset -- the ruleset to use for rewriting.
91538032Speter**		reclevel -- recursion level (to catch loops).
91638032Speter**		e -- the current envelope.
91790792Sgshapiro**		maxatom -- maximum length of buffer (usually MAXATOM)
91838032Speter**
91938032Speter**	Returns:
92038032Speter**		A status code.  If EX_TEMPFAIL, higher level code should
92138032Speter**			attempt recovery.
92238032Speter**
92338032Speter**	Side Effects:
92438032Speter**		pvp is modified.
92538032Speter*/
92638032Speter
92738032Speterstruct match
92838032Speter{
92964562Sgshapiro	char	**match_first;		/* first token matched */
93064562Sgshapiro	char	**match_last;		/* last token matched */
93164562Sgshapiro	char	**match_pattern;	/* pointer to pattern */
93238032Speter};
93338032Speter
93438032Speterint
93590792Sgshapirorewrite(pvp, ruleset, reclevel, e, maxatom)
93638032Speter	char **pvp;
93738032Speter	int ruleset;
93838032Speter	int reclevel;
93938032Speter	register ENVELOPE *e;
94090792Sgshapiro	int maxatom;
94138032Speter{
94238032Speter	register char *ap;		/* address pointer */
94338032Speter	register char *rp;		/* rewrite pointer */
94464562Sgshapiro	register char *rulename;	/* ruleset name */
94564562Sgshapiro	register char *prefix;
94638032Speter	register char **avp;		/* address vector pointer */
94738032Speter	register char **rvp;		/* rewrite vector pointer */
94838032Speter	register struct match *mlp;	/* cur ptr into mlist */
94938032Speter	register struct rewrite *rwr;	/* pointer to current rewrite rule */
95038032Speter	int ruleno;			/* current rule number */
95138032Speter	int rstat = EX_OK;		/* return status */
95238032Speter	int loopcount;
95338032Speter	struct match mlist[MAXMATCH];	/* stores match on LHS */
95464562Sgshapiro	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */
95538032Speter	char buf[MAXLINE];
95664562Sgshapiro	char name[6];
95738032Speter
95864562Sgshapiro	if (ruleset < 0 || ruleset >= MAXRWSETS)
95938032Speter	{
96064562Sgshapiro		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
96164562Sgshapiro		return EX_CONFIG;
96264562Sgshapiro	}
96364562Sgshapiro	rulename = RuleSetNames[ruleset];
96464562Sgshapiro	if (rulename == NULL)
96564562Sgshapiro	{
96690792Sgshapiro		(void) sm_snprintf(name, sizeof name, "%d", ruleset);
96764562Sgshapiro		rulename = name;
96864562Sgshapiro	}
96964562Sgshapiro	if (OpMode == MD_TEST)
97064562Sgshapiro		prefix = "";
97164562Sgshapiro	else
97264562Sgshapiro		prefix = "rewrite: ruleset ";
97364562Sgshapiro	if (OpMode == MD_TEST)
97464562Sgshapiro	{
97590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
97690792Sgshapiro				     "%s%-16.16s   input:", prefix, rulename);
97738032Speter		printav(pvp);
97838032Speter	}
97964562Sgshapiro	else if (tTd(21, 1))
98038032Speter	{
98190792Sgshapiro		sm_dprintf("%s%-16.16s   input:", prefix, rulename);
98264562Sgshapiro		printav(pvp);
98338032Speter	}
98438032Speter	if (reclevel++ > MaxRuleRecursion)
98538032Speter	{
98664562Sgshapiro		syserr("rewrite: excessive recursion (max %d), ruleset %s",
98764562Sgshapiro			MaxRuleRecursion, rulename);
98838032Speter		return EX_CONFIG;
98938032Speter	}
99038032Speter	if (pvp == NULL)
99138032Speter		return EX_USAGE;
99238032Speter
99338032Speter	/*
99438032Speter	**  Run through the list of rewrite rules, applying
99538032Speter	**	any that match.
99638032Speter	*/
99738032Speter
99838032Speter	ruleno = 1;
99938032Speter	loopcount = 0;
100038032Speter	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
100138032Speter	{
100264562Sgshapiro		int status;
100338032Speter
100438032Speter		/* if already canonical, quit now */
100538032Speter		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
100638032Speter			break;
100738032Speter
100838032Speter		if (tTd(21, 12))
100938032Speter		{
101064562Sgshapiro			if (tTd(21, 15))
101190792Sgshapiro				sm_dprintf("-----trying rule (line %d):",
101264562Sgshapiro				       rwr->r_line);
101364562Sgshapiro			else
101490792Sgshapiro				sm_dprintf("-----trying rule:");
101538032Speter			printav(rwr->r_lhs);
101638032Speter		}
101738032Speter
101838032Speter		/* try to match on this rule */
101938032Speter		mlp = mlist;
102038032Speter		rvp = rwr->r_lhs;
102138032Speter		avp = pvp;
102238032Speter		if (++loopcount > 100)
102338032Speter		{
102464562Sgshapiro			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
102564562Sgshapiro				rulename, ruleno);
102638032Speter			if (tTd(21, 1))
102738032Speter			{
102890792Sgshapiro				sm_dprintf("workspace: ");
102938032Speter				printav(pvp);
103038032Speter			}
103138032Speter			break;
103238032Speter		}
103338032Speter
103438032Speter		while ((ap = *avp) != NULL || *rvp != NULL)
103538032Speter		{
103638032Speter			rp = *rvp;
103738032Speter			if (tTd(21, 35))
103838032Speter			{
103990792Sgshapiro				sm_dprintf("ADVANCE rp=");
104038032Speter				xputs(rp);
104190792Sgshapiro				sm_dprintf(", ap=");
104238032Speter				xputs(ap);
104390792Sgshapiro				sm_dprintf("\n");
104438032Speter			}
104538032Speter			if (rp == NULL)
104638032Speter			{
104738032Speter				/* end-of-pattern before end-of-address */
104838032Speter				goto backup;
104938032Speter			}
105038032Speter			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
105138032Speter			    (*rp & 0377) != MATCHZERO)
105238032Speter			{
105338032Speter				/* end-of-input with patterns left */
105438032Speter				goto backup;
105538032Speter			}
105638032Speter
105738032Speter			switch (*rp & 0377)
105838032Speter			{
105938032Speter			  case MATCHCLASS:
106038032Speter				/* match any phrase in a class */
106164562Sgshapiro				mlp->match_pattern = rvp;
106264562Sgshapiro				mlp->match_first = avp;
106338032Speter	extendclass:
106438032Speter				ap = *avp;
106538032Speter				if (ap == NULL)
106638032Speter					goto backup;
106764562Sgshapiro				mlp->match_last = avp++;
106864562Sgshapiro				cataddr(mlp->match_first, mlp->match_last,
106964562Sgshapiro					buf, sizeof buf, '\0');
107038032Speter				if (!wordinclass(buf, rp[1]))
107138032Speter				{
107238032Speter					if (tTd(21, 36))
107338032Speter					{
107490792Sgshapiro						sm_dprintf("EXTEND  rp=");
107538032Speter						xputs(rp);
107690792Sgshapiro						sm_dprintf(", ap=");
107738032Speter						xputs(ap);
107890792Sgshapiro						sm_dprintf("\n");
107938032Speter					}
108038032Speter					goto extendclass;
108138032Speter				}
108238032Speter				if (tTd(21, 36))
108390792Sgshapiro					sm_dprintf("CLMATCH\n");
108438032Speter				mlp++;
108538032Speter				break;
108638032Speter
108738032Speter			  case MATCHNCLASS:
108838032Speter				/* match any token not in a class */
108938032Speter				if (wordinclass(ap, rp[1]))
109038032Speter					goto backup;
109138032Speter
109264562Sgshapiro				/* FALLTHROUGH */
109338032Speter
109438032Speter			  case MATCHONE:
109538032Speter			  case MATCHANY:
109638032Speter				/* match exactly one token */
109764562Sgshapiro				mlp->match_pattern = rvp;
109864562Sgshapiro				mlp->match_first = avp;
109964562Sgshapiro				mlp->match_last = avp++;
110038032Speter				mlp++;
110138032Speter				break;
110238032Speter
110338032Speter			  case MATCHZANY:
110438032Speter				/* match zero or more tokens */
110564562Sgshapiro				mlp->match_pattern = rvp;
110664562Sgshapiro				mlp->match_first = avp;
110764562Sgshapiro				mlp->match_last = avp - 1;
110838032Speter				mlp++;
110938032Speter				break;
111038032Speter
111138032Speter			  case MATCHZERO:
111238032Speter				/* match zero tokens */
111338032Speter				break;
111438032Speter
111538032Speter			  case MACRODEXPAND:
111638032Speter				/*
111738032Speter				**  Match against run-time macro.
111838032Speter				**  This algorithm is broken for the
111938032Speter				**  general case (no recursive macros,
112038032Speter				**  improper tokenization) but should
112138032Speter				**  work for the usual cases.
112238032Speter				*/
112338032Speter
112438032Speter				ap = macvalue(rp[1], e);
112564562Sgshapiro				mlp->match_first = avp;
112638032Speter				if (tTd(21, 2))
112798841Sgshapiro					sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",
112838032Speter						macname(rp[1]),
112938032Speter						ap == NULL ? "(NULL)" : ap);
113038032Speter
113138032Speter				if (ap == NULL)
113238032Speter					break;
113338032Speter				while (*ap != '\0')
113438032Speter				{
113538032Speter					if (*avp == NULL ||
113690792Sgshapiro					    sm_strncasecmp(ap, *avp,
113790792Sgshapiro							   strlen(*avp)) != 0)
113838032Speter					{
113938032Speter						/* no match */
114064562Sgshapiro						avp = mlp->match_first;
114138032Speter						goto backup;
114238032Speter					}
114338032Speter					ap += strlen(*avp++);
114438032Speter				}
114538032Speter
114638032Speter				/* match */
114738032Speter				break;
114838032Speter
114938032Speter			  default:
115038032Speter				/* must have exact match */
115138032Speter				if (sm_strcasecmp(rp, ap))
115238032Speter					goto backup;
115338032Speter				avp++;
115438032Speter				break;
115538032Speter			}
115638032Speter
115738032Speter			/* successful match on this token */
115838032Speter			rvp++;
115938032Speter			continue;
116038032Speter
116138032Speter	  backup:
116238032Speter			/* match failed -- back up */
116338032Speter			while (--mlp >= mlist)
116438032Speter			{
116564562Sgshapiro				rvp = mlp->match_pattern;
116638032Speter				rp = *rvp;
116764562Sgshapiro				avp = mlp->match_last + 1;
116838032Speter				ap = *avp;
116938032Speter
117038032Speter				if (tTd(21, 36))
117138032Speter				{
117290792Sgshapiro					sm_dprintf("BACKUP  rp=");
117338032Speter					xputs(rp);
117490792Sgshapiro					sm_dprintf(", ap=");
117538032Speter					xputs(ap);
117690792Sgshapiro					sm_dprintf("\n");
117738032Speter				}
117838032Speter
117938032Speter				if (ap == NULL)
118038032Speter				{
118138032Speter					/* run off the end -- back up again */
118238032Speter					continue;
118338032Speter				}
118438032Speter				if ((*rp & 0377) == MATCHANY ||
118538032Speter				    (*rp & 0377) == MATCHZANY)
118638032Speter				{
118738032Speter					/* extend binding and continue */
118864562Sgshapiro					mlp->match_last = avp++;
118938032Speter					rvp++;
119038032Speter					mlp++;
119138032Speter					break;
119238032Speter				}
119338032Speter				if ((*rp & 0377) == MATCHCLASS)
119438032Speter				{
119538032Speter					/* extend binding and try again */
119664562Sgshapiro					mlp->match_last = avp;
119738032Speter					goto extendclass;
119838032Speter				}
119938032Speter			}
120038032Speter
120138032Speter			if (mlp < mlist)
120238032Speter			{
120338032Speter				/* total failure to match */
120438032Speter				break;
120538032Speter			}
120638032Speter		}
120738032Speter
120838032Speter		/*
120938032Speter		**  See if we successfully matched
121038032Speter		*/
121138032Speter
121238032Speter		if (mlp < mlist || *rvp != NULL)
121338032Speter		{
121438032Speter			if (tTd(21, 10))
121590792Sgshapiro				sm_dprintf("----- rule fails\n");
121638032Speter			rwr = rwr->r_next;
121738032Speter			ruleno++;
121838032Speter			loopcount = 0;
121938032Speter			continue;
122038032Speter		}
122138032Speter
122238032Speter		rvp = rwr->r_rhs;
122338032Speter		if (tTd(21, 12))
122438032Speter		{
122590792Sgshapiro			sm_dprintf("-----rule matches:");
122638032Speter			printav(rvp);
122738032Speter		}
122838032Speter
122938032Speter		rp = *rvp;
123064562Sgshapiro		if (rp != NULL)
123138032Speter		{
123264562Sgshapiro			if ((*rp & 0377) == CANONUSER)
123364562Sgshapiro			{
123464562Sgshapiro				rvp++;
123564562Sgshapiro				rwr = rwr->r_next;
123664562Sgshapiro				ruleno++;
123764562Sgshapiro				loopcount = 0;
123864562Sgshapiro			}
123964562Sgshapiro			else if ((*rp & 0377) == CANONHOST)
124064562Sgshapiro			{
124164562Sgshapiro				rvp++;
124264562Sgshapiro				rwr = NULL;
124364562Sgshapiro			}
124438032Speter		}
124538032Speter
124638032Speter		/* substitute */
124738032Speter		for (avp = npvp; *rvp != NULL; rvp++)
124838032Speter		{
124938032Speter			register struct match *m;
125038032Speter			register char **pp;
125138032Speter
125238032Speter			rp = *rvp;
125338032Speter			if ((*rp & 0377) == MATCHREPL)
125438032Speter			{
125538032Speter				/* substitute from LHS */
125638032Speter				m = &mlist[rp[1] - '1'];
125738032Speter				if (m < mlist || m >= mlp)
125838032Speter				{
125964562Sgshapiro					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
126064562Sgshapiro						rulename, rp[1]);
126138032Speter					return EX_CONFIG;
126238032Speter				}
126338032Speter				if (tTd(21, 15))
126438032Speter				{
126590792Sgshapiro					sm_dprintf("$%c:", rp[1]);
126664562Sgshapiro					pp = m->match_first;
126764562Sgshapiro					while (pp <= m->match_last)
126838032Speter					{
126990792Sgshapiro						sm_dprintf(" %p=\"", *pp);
127090792Sgshapiro						sm_dflush();
127190792Sgshapiro						sm_dprintf("%s\"", *pp++);
127238032Speter					}
127390792Sgshapiro					sm_dprintf("\n");
127438032Speter				}
127564562Sgshapiro				pp = m->match_first;
127664562Sgshapiro				while (pp <= m->match_last)
127738032Speter				{
127890792Sgshapiro					if (avp >= &npvp[maxatom])
127938032Speter					{
128064562Sgshapiro						syserr("554 5.3.0 rewrite: expansion too long");
128190792Sgshapiro						if (LogLevel > 9)
128290792Sgshapiro							sm_syslog(LOG_ERR,
128390792Sgshapiro								e->e_id,
128490792Sgshapiro								"rewrite: expansion too long, ruleset=%s, ruleno=%d",
128590792Sgshapiro								rulename,
128690792Sgshapiro								ruleno);
128738032Speter						return EX_DATAERR;
128838032Speter					}
128938032Speter					*avp++ = *pp++;
129038032Speter				}
129138032Speter			}
129238032Speter			else
129338032Speter			{
129438032Speter				/* some sort of replacement */
129590792Sgshapiro				if (avp >= &npvp[maxatom])
129638032Speter				{
129738032Speter	toolong:
129864562Sgshapiro					syserr("554 5.3.0 rewrite: expansion too long");
129990792Sgshapiro					if (LogLevel > 9)
130090792Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
130190792Sgshapiro							"rewrite: expansion too long, ruleset=%s, ruleno=%d",
130290792Sgshapiro							rulename, ruleno);
130338032Speter					return EX_DATAERR;
130438032Speter				}
130538032Speter				if ((*rp & 0377) != MACRODEXPAND)
130638032Speter				{
130738032Speter					/* vanilla replacement */
130838032Speter					*avp++ = rp;
130938032Speter				}
131038032Speter				else
131138032Speter				{
131298841Sgshapiro					/* $&{x} replacement */
131338032Speter					char *mval = macvalue(rp[1], e);
131438032Speter					char **xpvp;
131538032Speter					int trsize = 0;
131638032Speter					static size_t pvpb1_size = 0;
131738032Speter					static char **pvpb1 = NULL;
131838032Speter					char pvpbuf[PSBUFSIZE];
131938032Speter
132038032Speter					if (tTd(21, 2))
132198841Sgshapiro						sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
132238032Speter							macname(rp[1]),
132338032Speter							mval == NULL ? "(NULL)" : mval);
132438032Speter					if (mval == NULL || *mval == '\0')
132538032Speter						continue;
132638032Speter
132738032Speter					/* save the remainder of the input */
132838032Speter					for (xpvp = pvp; *xpvp != NULL; xpvp++)
132938032Speter						trsize += sizeof *xpvp;
133064562Sgshapiro					if ((size_t) trsize > pvpb1_size)
133138032Speter					{
133238032Speter						if (pvpb1 != NULL)
133377349Sgshapiro							sm_free(pvpb1);
133490792Sgshapiro						pvpb1 = (char **)
133590792Sgshapiro							sm_pmalloc_x(trsize);
133638032Speter						pvpb1_size = trsize;
133738032Speter					}
133838032Speter
133964562Sgshapiro					memmove((char *) pvpb1,
134064562Sgshapiro						(char *) pvp,
134164562Sgshapiro						trsize);
134238032Speter
134338032Speter					/* scan the new replacement */
134438032Speter					xpvp = prescan(mval, '\0', pvpbuf,
134564562Sgshapiro						       sizeof pvpbuf, NULL,
134664562Sgshapiro						       NULL);
134738032Speter					if (xpvp == NULL)
134838032Speter					{
134938032Speter						/* prescan pre-printed error */
135038032Speter						return EX_DATAERR;
135138032Speter					}
135238032Speter
135338032Speter					/* insert it into the output stream */
135438032Speter					while (*xpvp != NULL)
135538032Speter					{
135638032Speter						if (tTd(21, 19))
135790792Sgshapiro							sm_dprintf(" ... %s\n",
135864562Sgshapiro								*xpvp);
135990792Sgshapiro						*avp++ = sm_rpool_strdup_x(
136090792Sgshapiro							e->e_rpool, *xpvp);
136190792Sgshapiro						if (avp >= &npvp[maxatom])
136238032Speter							goto toolong;
136338032Speter						xpvp++;
136438032Speter					}
136538032Speter					if (tTd(21, 19))
136690792Sgshapiro						sm_dprintf(" ... DONE\n");
136738032Speter
136838032Speter					/* restore the old trailing input */
136964562Sgshapiro					memmove((char *) pvp,
137064562Sgshapiro						(char *) pvpb1,
137164562Sgshapiro						trsize);
137238032Speter				}
137338032Speter			}
137438032Speter		}
137538032Speter		*avp++ = NULL;
137638032Speter
137738032Speter		/*
137838032Speter		**  Check for any hostname/keyword lookups.
137938032Speter		*/
138038032Speter
138138032Speter		for (rvp = npvp; *rvp != NULL; rvp++)
138238032Speter		{
138338032Speter			char **hbrvp;
138438032Speter			char **xpvp;
138538032Speter			int trsize;
138638032Speter			char *replac;
138738032Speter			int endtoken;
138838032Speter			STAB *map;
138938032Speter			char *mapname;
139038032Speter			char **key_rvp;
139138032Speter			char **arg_rvp;
139238032Speter			char **default_rvp;
139364562Sgshapiro			char cbuf[MAXNAME + 1];
139438032Speter			char *pvpb1[MAXATOM + 1];
139538032Speter			char *argvect[10];
139638032Speter			char pvpbuf[PSBUFSIZE];
139738032Speter			char *nullpvp[1];
139838032Speter
139938032Speter			if ((**rvp & 0377) != HOSTBEGIN &&
140038032Speter			    (**rvp & 0377) != LOOKUPBEGIN)
140138032Speter				continue;
140238032Speter
140338032Speter			/*
140438032Speter			**  Got a hostname/keyword lookup.
140538032Speter			**
140638032Speter			**	This could be optimized fairly easily.
140738032Speter			*/
140838032Speter
140938032Speter			hbrvp = rvp;
141038032Speter			if ((**rvp & 0377) == HOSTBEGIN)
141138032Speter			{
141238032Speter				endtoken = HOSTEND;
141338032Speter				mapname = "host";
141438032Speter			}
141538032Speter			else
141638032Speter			{
141738032Speter				endtoken = LOOKUPEND;
141838032Speter				mapname = *++rvp;
141938032Speter			}
142038032Speter			map = stab(mapname, ST_MAP, ST_FIND);
142138032Speter			if (map == NULL)
142264562Sgshapiro				syserr("554 5.3.0 rewrite: map %s not found", mapname);
142338032Speter
142438032Speter			/* extract the match part */
142538032Speter			key_rvp = ++rvp;
142638032Speter			default_rvp = NULL;
142738032Speter			arg_rvp = argvect;
142838032Speter			xpvp = NULL;
142938032Speter			replac = pvpbuf;
143038032Speter			while (*rvp != NULL && (**rvp & 0377) != endtoken)
143138032Speter			{
143238032Speter				int nodetype = **rvp & 0377;
143338032Speter
143438032Speter				if (nodetype != CANONHOST && nodetype != CANONUSER)
143538032Speter				{
143638032Speter					rvp++;
143738032Speter					continue;
143838032Speter				}
143938032Speter
144038032Speter				*rvp++ = NULL;
144138032Speter
144238032Speter				if (xpvp != NULL)
144338032Speter				{
144438032Speter					cataddr(xpvp, NULL, replac,
144538032Speter						&pvpbuf[sizeof pvpbuf] - replac,
144638032Speter						'\0');
144738032Speter					*++arg_rvp = replac;
144838032Speter					replac += strlen(replac) + 1;
144938032Speter					xpvp = NULL;
145038032Speter				}
145138032Speter				switch (nodetype)
145238032Speter				{
145338032Speter				  case CANONHOST:
145438032Speter					xpvp = rvp;
145538032Speter					break;
145638032Speter
145738032Speter				  case CANONUSER:
145838032Speter					default_rvp = rvp;
145938032Speter					break;
146038032Speter				}
146138032Speter			}
146238032Speter			if (*rvp != NULL)
146338032Speter				*rvp++ = NULL;
146438032Speter			if (xpvp != NULL)
146538032Speter			{
146638032Speter				cataddr(xpvp, NULL, replac,
146738032Speter					&pvpbuf[sizeof pvpbuf] - replac,
146838032Speter					'\0');
146938032Speter				*++arg_rvp = replac;
147038032Speter			}
147138032Speter			*++arg_rvp = NULL;
147238032Speter
147338032Speter			/* save the remainder of the input string */
147438032Speter			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
147564562Sgshapiro			memmove((char *) pvpb1, (char *) rvp, trsize);
147638032Speter
147738032Speter			/* look it up */
147864562Sgshapiro			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
147964562Sgshapiro				map == NULL ? '\0' : map->s_map.map_spacesub);
148064562Sgshapiro			argvect[0] = cbuf;
148164562Sgshapiro			replac = map_lookup(map, cbuf, argvect, &rstat, e);
148238032Speter
148338032Speter			/* if no replacement, use default */
148438032Speter			if (replac == NULL && default_rvp != NULL)
148538032Speter			{
148638032Speter				/* create the default */
148764562Sgshapiro				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
148864562Sgshapiro				replac = cbuf;
148938032Speter			}
149038032Speter
149138032Speter			if (replac == NULL)
149238032Speter			{
149338032Speter				xpvp = key_rvp;
149438032Speter			}
149538032Speter			else if (*replac == '\0')
149638032Speter			{
149738032Speter				/* null replacement */
149838032Speter				nullpvp[0] = NULL;
149938032Speter				xpvp = nullpvp;
150038032Speter			}
150138032Speter			else
150238032Speter			{
150338032Speter				/* scan the new replacement */
150438032Speter				xpvp = prescan(replac, '\0', pvpbuf,
150538032Speter					       sizeof pvpbuf, NULL, NULL);
150638032Speter				if (xpvp == NULL)
150738032Speter				{
150838032Speter					/* prescan already printed error */
150938032Speter					return EX_DATAERR;
151038032Speter				}
151138032Speter			}
151238032Speter
151338032Speter			/* append it to the token list */
151438032Speter			for (avp = hbrvp; *xpvp != NULL; xpvp++)
151538032Speter			{
151690792Sgshapiro				*avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
151790792Sgshapiro				if (avp >= &npvp[maxatom])
151838032Speter					goto toolong;
151938032Speter			}
152038032Speter
152138032Speter			/* restore the old trailing information */
152238032Speter			rvp = avp - 1;
152338032Speter			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
152490792Sgshapiro				if (avp >= &npvp[maxatom])
152538032Speter					goto toolong;
152638032Speter		}
152738032Speter
152838032Speter		/*
152938032Speter		**  Check for subroutine calls.
153038032Speter		*/
153138032Speter
153264562Sgshapiro		status = callsubr(npvp, reclevel, e);
153364562Sgshapiro		if (rstat == EX_OK || status == EX_TEMPFAIL)
153464562Sgshapiro			rstat = status;
153538032Speter
153638032Speter		/* copy vector back into original space. */
153738032Speter		for (avp = npvp; *avp++ != NULL;)
153838032Speter			continue;
153964562Sgshapiro		memmove((char *) pvp, (char *) npvp,
154038032Speter		      (int) (avp - npvp) * sizeof *avp);
154164562Sgshapiro
154238032Speter		if (tTd(21, 4))
154338032Speter		{
154490792Sgshapiro			sm_dprintf("rewritten as:");
154538032Speter			printav(pvp);
154638032Speter		}
154738032Speter	}
154838032Speter
154964562Sgshapiro	if (OpMode == MD_TEST)
155038032Speter	{
155190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
155290792Sgshapiro				     "%s%-16.16s returns:", prefix, rulename);
155338032Speter		printav(pvp);
155438032Speter	}
155564562Sgshapiro	else if (tTd(21, 1))
155664562Sgshapiro	{
155790792Sgshapiro		sm_dprintf("%s%-16.16s returns:", prefix, rulename);
155864562Sgshapiro		printav(pvp);
155964562Sgshapiro	}
156038032Speter	return rstat;
156138032Speter}
156290792Sgshapiro/*
156338032Speter**  CALLSUBR -- call subroutines in rewrite vector
156438032Speter**
156538032Speter**	Parameters:
156638032Speter**		pvp -- pointer to token vector.
156738032Speter**		reclevel -- the current recursion level.
156838032Speter**		e -- the current envelope.
156938032Speter**
157038032Speter**	Returns:
157138032Speter**		The status from the subroutine call.
157238032Speter**
157338032Speter**	Side Effects:
157438032Speter**		pvp is modified.
157538032Speter*/
157638032Speter
157764562Sgshapirostatic int
157838032Spetercallsubr(pvp, reclevel, e)
157938032Speter	char **pvp;
158038032Speter	int reclevel;
158138032Speter	ENVELOPE *e;
158238032Speter{
158364562Sgshapiro	char **avp;
158438032Speter	register int i;
158590792Sgshapiro	int subr, j;
158690792Sgshapiro	int nsubr;
158764562Sgshapiro	int status;
158838032Speter	int rstat = EX_OK;
158990792Sgshapiro#define MAX_SUBR	16
159090792Sgshapiro	int subrnumber[MAX_SUBR];
159190792Sgshapiro	int subrindex[MAX_SUBR];
159238032Speter
159390792Sgshapiro	nsubr = 0;
159490792Sgshapiro
159590792Sgshapiro	/*
159690792Sgshapiro	**  Look for subroutine calls in pvp, collect them into subr*[]
159790792Sgshapiro	**  We will perform the calls in the next loop, because we will
159890792Sgshapiro	**  call the "last" subroutine first to avoid recursive calls
159990792Sgshapiro	**  and too much copying.
160090792Sgshapiro	*/
160190792Sgshapiro
160290792Sgshapiro	for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
160338032Speter	{
160438032Speter		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
160538032Speter		{
160638032Speter			stripquotes(avp[1]);
160738032Speter			subr = strtorwset(avp[1], NULL, ST_FIND);
160838032Speter			if (subr < 0)
160938032Speter			{
161090792Sgshapiro				syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
161138032Speter				return EX_CONFIG;
161238032Speter			}
161338032Speter
161438032Speter			/*
161590792Sgshapiro			**  XXX instead of doing this we could optimize
161690792Sgshapiro			**  the rules after reading them: just remove
161790792Sgshapiro			**  calls to empty rulesets
161838032Speter			*/
161938032Speter
162090792Sgshapiro			/* subroutine is an empty ruleset?  don't call it */
162190792Sgshapiro			if (RewriteRules[subr] == NULL)
162290792Sgshapiro			{
162390792Sgshapiro				if (tTd(21, 3))
162490792Sgshapiro					sm_dprintf("-----skip subr %s (%d)\n",
162590792Sgshapiro						avp[1], subr);
162690792Sgshapiro				for (i = 2; avp[i] != NULL; i++)
162790792Sgshapiro					avp[i - 2] = avp[i];
162890792Sgshapiro				avp[i - 2] = NULL;
162938032Speter				continue;
163090792Sgshapiro			}
163190792Sgshapiro			if (++nsubr >= MAX_SUBR)
163238032Speter			{
163390792Sgshapiro				syserr("554 5.3.0 Too many subroutine calls (%d max)",
163490792Sgshapiro					MAX_SUBR);
163590792Sgshapiro				return EX_CONFIG;
163638032Speter			}
163790792Sgshapiro			subrnumber[nsubr] = subr;
163890792Sgshapiro			subrindex[nsubr] = j;
163990792Sgshapiro		}
164090792Sgshapiro	}
164138032Speter
164290792Sgshapiro	/*
164390792Sgshapiro	**  Perform the actual subroutines calls, "last" one first, i.e.,
164490792Sgshapiro	**  go from the right to the left through all calls,
164590792Sgshapiro	**  do the rewriting in place.
164690792Sgshapiro	*/
164738032Speter
164890792Sgshapiro	for (; nsubr > 0; nsubr--)
164990792Sgshapiro	{
165090792Sgshapiro		subr = subrnumber[nsubr];
165190792Sgshapiro		avp = pvp + subrindex[nsubr];
165238032Speter
165390792Sgshapiro		/* remove the subroutine call and name */
165490792Sgshapiro		for (i = 2; avp[i] != NULL; i++)
165590792Sgshapiro			avp[i - 2] = avp[i];
165690792Sgshapiro		avp[i - 2] = NULL;
165738032Speter
165890792Sgshapiro		/*
165990792Sgshapiro		**  Now we need to call the ruleset specified for
166090792Sgshapiro		**  the subroutine. we can do this inplace since
166190792Sgshapiro		**  we call the "last" subroutine first.
166290792Sgshapiro		*/
166390792Sgshapiro
166490792Sgshapiro		status = rewrite(avp, subr, reclevel, e,
166590792Sgshapiro				MAXATOM - subrindex[nsubr]);
166690792Sgshapiro		if (status != EX_OK && status != EX_TEMPFAIL)
166790792Sgshapiro			return status;
166890792Sgshapiro		if (rstat == EX_OK || status == EX_TEMPFAIL)
166990792Sgshapiro			rstat = status;
167038032Speter	}
167138032Speter	return rstat;
167238032Speter}
167390792Sgshapiro/*
167438032Speter**  MAP_LOOKUP -- do lookup in map
167538032Speter**
167638032Speter**	Parameters:
167790792Sgshapiro**		smap -- the map to use for the lookup.
167838032Speter**		key -- the key to look up.
167938032Speter**		argvect -- arguments to pass to the map lookup.
168038032Speter**		pstat -- a pointer to an integer in which to store the
168138032Speter**			status from the lookup.
168238032Speter**		e -- the current envelope.
168338032Speter**
168438032Speter**	Returns:
168538032Speter**		The result of the lookup.
168638032Speter**		NULL -- if there was no data for the given key.
168738032Speter*/
168838032Speter
168964562Sgshapirostatic char *
169064562Sgshapiromap_lookup(smap, key, argvect, pstat, e)
169164562Sgshapiro	STAB *smap;
169238032Speter	char key[];
169338032Speter	char **argvect;
169438032Speter	int *pstat;
169538032Speter	ENVELOPE *e;
169638032Speter{
169764562Sgshapiro	auto int status = EX_OK;
169864562Sgshapiro	MAP *map;
169938032Speter	char *replac;
170038032Speter
170164562Sgshapiro	if (smap == NULL)
170264562Sgshapiro		return NULL;
170364562Sgshapiro
170464562Sgshapiro	map = &smap->s_map;
170564562Sgshapiro	DYNOPENMAP(map);
170664562Sgshapiro
170764562Sgshapiro	if (e->e_sendmode == SM_DEFER &&
170864562Sgshapiro	    bitset(MF_DEFER, map->map_mflags))
170938032Speter	{
171038032Speter		/* don't do any map lookups */
171138032Speter		if (tTd(60, 1))
171290792Sgshapiro			sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
171364562Sgshapiro				smap->s_name, key);
171438032Speter		*pstat = EX_TEMPFAIL;
171538032Speter		return NULL;
171638032Speter	}
171738032Speter
171864562Sgshapiro	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
171938032Speter		stripquotes(key);
172038032Speter
172138032Speter	if (tTd(60, 1))
172242575Speter	{
172390792Sgshapiro		sm_dprintf("map_lookup(%s, %s", smap->s_name, key);
172442575Speter		if (tTd(60, 5))
172542575Speter		{
172642575Speter			int i;
172742575Speter
172842575Speter			for (i = 0; argvect[i] != NULL; i++)
172990792Sgshapiro				sm_dprintf(", %%%d=%s", i, argvect[i]);
173042575Speter		}
173190792Sgshapiro		sm_dprintf(") => ");
173242575Speter	}
173364562Sgshapiro	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
173438032Speter	if (tTd(60, 1))
173590792Sgshapiro		sm_dprintf("%s (%d)\n",
173638032Speter			replac != NULL ? replac : "NOT FOUND",
173764562Sgshapiro			status);
173838032Speter
173964562Sgshapiro	/* should recover if status == EX_TEMPFAIL */
174064562Sgshapiro	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
174138032Speter	{
174238032Speter		*pstat = EX_TEMPFAIL;
174338032Speter		if (tTd(60, 1))
174490792Sgshapiro			sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
174564562Sgshapiro				smap->s_name, key, errno);
174638032Speter		if (e->e_message == NULL)
174738032Speter		{
174838032Speter			char mbuf[320];
174938032Speter
175090792Sgshapiro			(void) sm_snprintf(mbuf, sizeof mbuf,
175138032Speter				"%.80s map: lookup (%s): deferred",
175264562Sgshapiro				smap->s_name,
175338032Speter				shortenstring(key, MAXSHORTSTR));
175490792Sgshapiro			e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
175538032Speter		}
175638032Speter	}
175764562Sgshapiro	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
175838032Speter	{
175964562Sgshapiro		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
176038032Speter		static char *rwbuf = NULL;
176138032Speter		static size_t rwbuflen = 0;
176238032Speter
176338032Speter		if (i > rwbuflen)
176438032Speter		{
176538032Speter			if (rwbuf != NULL)
176677349Sgshapiro				sm_free(rwbuf);
176738032Speter			rwbuflen = i;
176890792Sgshapiro			rwbuf = (char *) sm_pmalloc_x(rwbuflen);
176938032Speter		}
177090792Sgshapiro		(void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
177138032Speter		if (tTd(60, 4))
177290792Sgshapiro			sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
177338032Speter				rwbuf);
177438032Speter		return rwbuf;
177538032Speter	}
177638032Speter	return replac;
177738032Speter}
177890792Sgshapiro/*
177964562Sgshapiro**  INITERRMAILERS -- initialize error and discard mailers
178064562Sgshapiro**
178164562Sgshapiro**	Parameters:
178264562Sgshapiro**		none.
178364562Sgshapiro**
178464562Sgshapiro**	Returns:
178564562Sgshapiro**		none.
178664562Sgshapiro**
178764562Sgshapiro**	Side Effects:
178864562Sgshapiro**		initializes error and discard mailers.
178964562Sgshapiro*/
179064562Sgshapiro
179164562Sgshapirostatic MAILER discardmailer;
179264562Sgshapirostatic MAILER errormailer;
179364562Sgshapirostatic char *discardargv[] = { "DISCARD", NULL };
179464562Sgshapirostatic char *errorargv[] = { "ERROR", NULL };
179564562Sgshapiro
179664562Sgshapirovoid
179764562Sgshapiroiniterrmailers()
179864562Sgshapiro{
179964562Sgshapiro	if (discardmailer.m_name == NULL)
180064562Sgshapiro	{
180164562Sgshapiro		/* initialize the discard mailer */
180264562Sgshapiro		discardmailer.m_name = "*discard*";
180364562Sgshapiro		discardmailer.m_mailer = "DISCARD";
180464562Sgshapiro		discardmailer.m_argv = discardargv;
180564562Sgshapiro	}
180664562Sgshapiro	if (errormailer.m_name == NULL)
180764562Sgshapiro	{
180864562Sgshapiro		/* initialize the bogus mailer */
180964562Sgshapiro		errormailer.m_name = "*error*";
181064562Sgshapiro		errormailer.m_mailer = "ERROR";
181164562Sgshapiro		errormailer.m_argv = errorargv;
181264562Sgshapiro	}
181364562Sgshapiro}
181490792Sgshapiro/*
181538032Speter**  BUILDADDR -- build address from token vector.
181638032Speter**
181738032Speter**	Parameters:
181838032Speter**		tv -- token vector.
181938032Speter**		a -- pointer to address descriptor to fill.
182038032Speter**			If NULL, one will be allocated.
182138032Speter**		flags -- info regarding whether this is a sender or
182238032Speter**			a recipient.
182338032Speter**		e -- the current envelope.
182438032Speter**
182538032Speter**	Returns:
182638032Speter**		NULL if there was an error.
182738032Speter**		'a' otherwise.
182838032Speter**
182938032Speter**	Side Effects:
183038032Speter**		fills in 'a'
183138032Speter*/
183238032Speter
183364562Sgshapirostatic struct errcodes
183438032Speter{
183538032Speter	char	*ec_name;		/* name of error code */
183638032Speter	int	ec_code;		/* numeric code */
183738032Speter} ErrorCodes[] =
183838032Speter{
183938032Speter	{ "usage",		EX_USAGE	},
184038032Speter	{ "nouser",		EX_NOUSER	},
184138032Speter	{ "nohost",		EX_NOHOST	},
184238032Speter	{ "unavailable",	EX_UNAVAILABLE	},
184338032Speter	{ "software",		EX_SOFTWARE	},
184438032Speter	{ "tempfail",		EX_TEMPFAIL	},
184538032Speter	{ "protocol",		EX_PROTOCOL	},
184638032Speter	{ "config",		EX_CONFIG	},
184738032Speter	{ NULL,			EX_UNAVAILABLE	}
184838032Speter};
184938032Speter
185064562Sgshapirostatic ADDRESS *
185138032Speterbuildaddr(tv, a, flags, e)
185238032Speter	register char **tv;
185338032Speter	register ADDRESS *a;
185438032Speter	int flags;
185538032Speter	register ENVELOPE *e;
185638032Speter{
185794334Sgshapiro	bool tempfail = false;
185838032Speter	struct mailer **mp;
185938032Speter	register struct mailer *m;
186038032Speter	register char *p;
186138032Speter	char *mname;
186238032Speter	char **hostp;
186338032Speter	char hbuf[MAXNAME + 1];
186442575Speter	static char ubuf[MAXNAME + 2];
186538032Speter
186638032Speter	if (tTd(24, 5))
186738032Speter	{
186890792Sgshapiro		sm_dprintf("buildaddr, flags=%x, tv=", flags);
186938032Speter		printav(tv);
187038032Speter	}
187138032Speter
187238032Speter	if (a == NULL)
187390792Sgshapiro		a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
187464562Sgshapiro	memset((char *) a, '\0', sizeof *a);
187564562Sgshapiro	hbuf[0] = '\0';
187638032Speter
187738032Speter	/* set up default error return flags */
187838032Speter	a->q_flags |= DefaultNotify;
187938032Speter
188038032Speter	/* figure out what net/mailer to use */
188138032Speter	if (*tv == NULL || (**tv & 0377) != CANONNET)
188238032Speter	{
188364562Sgshapiro		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
188438032Speterbadaddr:
188594334Sgshapiro#if _FFR_ALLOW_S0_ERROR_4XX
188694334Sgshapiro		/*
188794334Sgshapiro		**  ExitStat may have been set by an earlier map open
188894334Sgshapiro		**  failure (to a permanent error (EX_OSERR) in syserr())
188994334Sgshapiro		**  so we also need to check if this particular $#error
189094334Sgshapiro		**  return wanted a 4XX failure.
189194334Sgshapiro		**
189294334Sgshapiro		**  XXX the real fix is probably to set ExitStat correctly,
189394334Sgshapiro		**  i.e., to EX_TEMPFAIL if the map open is just a temporary
189494334Sgshapiro		**  error.
189594334Sgshapiro		**
189694334Sgshapiro		**  tempfail is tested here even if _FFR_ALLOW_S0_ERROR_4XX
189794334Sgshapiro		**  is not set; that's ok because it is initialized to false.
189894334Sgshapiro		*/
189994334Sgshapiro#endif /* _FFR_ALLOW_S0_ERROR_4XX */
190094334Sgshapiro
190194334Sgshapiro		if (ExitStat == EX_TEMPFAIL || tempfail)
190264562Sgshapiro			a->q_state = QS_QUEUEUP;
190364562Sgshapiro		else
190438032Speter		{
190564562Sgshapiro			a->q_state = QS_BADADDR;
190664562Sgshapiro			a->q_mailer = &errormailer;
190738032Speter		}
190838032Speter		return a;
190938032Speter	}
191038032Speter	mname = *++tv;
191138032Speter
191238032Speter	/* extract host and user portions */
191338032Speter	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
191438032Speter		hostp = ++tv;
191538032Speter	else
191638032Speter		hostp = NULL;
191738032Speter	while (*tv != NULL && (**tv & 0377) != CANONUSER)
191838032Speter		tv++;
191938032Speter	if (*tv == NULL)
192038032Speter	{
192164562Sgshapiro		syserr("554 5.3.5 buildaddr: no user");
192238032Speter		goto badaddr;
192338032Speter	}
192438032Speter	if (tv == hostp)
192538032Speter		hostp = NULL;
192638032Speter	else if (hostp != NULL)
192738032Speter		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
192838032Speter	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
192938032Speter
193038032Speter	/* save away the host name */
193190792Sgshapiro	if (sm_strcasecmp(mname, "error") == 0)
193238032Speter	{
193364562Sgshapiro		/* Set up triplet for use by -bv */
193464562Sgshapiro		a->q_mailer = &errormailer;
193590792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
193690792Sgshapiro		/* XXX wrong place? */
193764562Sgshapiro
193838032Speter		if (hostp != NULL)
193938032Speter		{
194038032Speter			register struct errcodes *ep;
194138032Speter
194290792Sgshapiro			a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
194338032Speter			if (strchr(hbuf, '.') != NULL)
194438032Speter			{
194590792Sgshapiro				a->q_status = sm_rpool_strdup_x(e->e_rpool,
194690792Sgshapiro								hbuf);
194738032Speter				setstat(dsntoexitstat(hbuf));
194838032Speter			}
194938032Speter			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
195038032Speter			{
195138032Speter				setstat(atoi(hbuf));
195238032Speter			}
195338032Speter			else
195438032Speter			{
195538032Speter				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
195690792Sgshapiro					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
195738032Speter						break;
195838032Speter				setstat(ep->ec_code);
195938032Speter			}
196038032Speter		}
196138032Speter		else
196264562Sgshapiro		{
196364562Sgshapiro			a->q_host = NULL;
196438032Speter			setstat(EX_UNAVAILABLE);
196564562Sgshapiro		}
196638032Speter		stripquotes(ubuf);
196764562Sgshapiro		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
196838032Speter		{
196964562Sgshapiro			char fmt[16];
197064562Sgshapiro			int off;
197138032Speter
197264562Sgshapiro			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
197338032Speter			{
197464562Sgshapiro				ubuf[off + 4] = '\0';
197564562Sgshapiro				off += 5;
197638032Speter			}
197764562Sgshapiro			else
197864562Sgshapiro			{
197964562Sgshapiro				off = 4;
198064562Sgshapiro				ubuf[3] = '\0';
198164562Sgshapiro			}
198290792Sgshapiro			(void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s");
198364562Sgshapiro			if (off > 4)
198464562Sgshapiro				usrerr(fmt, ubuf + off);
198564562Sgshapiro			else if (isenhsc(hbuf, '\0') > 0)
198664562Sgshapiro				usrerrenh(hbuf, fmt, ubuf + off);
198764562Sgshapiro			else
198864562Sgshapiro				usrerr(fmt, ubuf + off);
198964562Sgshapiro			/* XXX ubuf[off - 1] = ' '; */
199094334Sgshapiro#if _FFR_ALLOW_S0_ERROR_4XX
199194334Sgshapiro			if (ubuf[0] == '4')
199294334Sgshapiro				tempfail = true;
199394334Sgshapiro#endif /* _FFR_ALLOW_S0_ERROR_4XX */
199438032Speter		}
199538032Speter		else
199638032Speter		{
199764562Sgshapiro			usrerr("553 5.3.0 %s", ubuf);
199838032Speter		}
199938032Speter		goto badaddr;
200038032Speter	}
200138032Speter
200238032Speter	for (mp = Mailer; (m = *mp++) != NULL; )
200338032Speter	{
200490792Sgshapiro		if (sm_strcasecmp(m->m_name, mname) == 0)
200538032Speter			break;
200638032Speter	}
200738032Speter	if (m == NULL)
200838032Speter	{
200964562Sgshapiro		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
201038032Speter		goto badaddr;
201138032Speter	}
201238032Speter	a->q_mailer = m;
201338032Speter
201438032Speter	/* figure out what host (if any) */
201538032Speter	if (hostp == NULL)
201638032Speter	{
201738032Speter		if (!bitnset(M_LOCALMAILER, m->m_flags))
201838032Speter		{
201964562Sgshapiro			syserr("554 5.3.5 buildaddr: no host");
202038032Speter			goto badaddr;
202138032Speter		}
202238032Speter		a->q_host = NULL;
202338032Speter	}
202438032Speter	else
202590792Sgshapiro		a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
202638032Speter
202738032Speter	/* figure out the user */
202838032Speter	p = ubuf;
202938032Speter	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
203038032Speter	{
203138032Speter		p++;
203238032Speter		tv++;
203338032Speter		a->q_flags |= QNOTREMOTE;
203438032Speter	}
203538032Speter
203638032Speter	/* do special mapping for local mailer */
203738032Speter	if (*p == '"')
203838032Speter		p++;
203938032Speter	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
204038032Speter		a->q_mailer = m = ProgMailer;
204138032Speter	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
204238032Speter		a->q_mailer = m = FileMailer;
204338032Speter	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
204438032Speter	{
204538032Speter		/* may be :include: */
204638032Speter		stripquotes(ubuf);
204790792Sgshapiro		if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
204838032Speter		{
204938032Speter			/* if :include:, don't need further rewriting */
205038032Speter			a->q_mailer = m = InclMailer;
205190792Sgshapiro			a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
205238032Speter			return a;
205338032Speter		}
205438032Speter	}
205538032Speter
205638032Speter	/* rewrite according recipient mailer rewriting rules */
205790792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
205864562Sgshapiro
205990792Sgshapiro	if (ConfigLevel >= 10 ||
206064562Sgshapiro	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
206138032Speter	{
206238032Speter		/* sender addresses done later */
206390792Sgshapiro		(void) REWRITE(tv, 2, e);
206438032Speter		if (m->m_re_rwset > 0)
206590792Sgshapiro		       (void) REWRITE(tv, m->m_re_rwset, e);
206638032Speter	}
206790792Sgshapiro	(void) REWRITE(tv, 4, e);
206838032Speter
206938032Speter	/* save the result for the command line/RCPT argument */
207038032Speter	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
207190792Sgshapiro	a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
207238032Speter
207338032Speter	/*
207438032Speter	**  Do mapping to lower case as requested by mailer
207538032Speter	*/
207638032Speter
207738032Speter	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
207838032Speter		makelower(a->q_host);
207938032Speter	if (!bitnset(M_USR_UPPER, m->m_flags))
208038032Speter		makelower(a->q_user);
208138032Speter
208238032Speter	if (tTd(24, 6))
208338032Speter	{
208490792Sgshapiro		sm_dprintf("buildaddr => ");
208590792Sgshapiro		printaddr(a, false);
208638032Speter	}
208738032Speter	return a;
208838032Speter}
208990792Sgshapiro/*
209038032Speter**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
209138032Speter**
209238032Speter**	Parameters:
209338032Speter**		pvp -- parameter vector to rebuild.
209438032Speter**		evp -- last parameter to include.  Can be NULL to
209538032Speter**			use entire pvp.
209638032Speter**		buf -- buffer to build the string into.
209738032Speter**		sz -- size of buf.
209890792Sgshapiro**		spacesub -- the space separator character; if '\0',
209938032Speter**			use SpaceSub.
210038032Speter**
210138032Speter**	Returns:
210238032Speter**		none.
210338032Speter**
210438032Speter**	Side Effects:
210538032Speter**		Destroys buf.
210638032Speter*/
210738032Speter
210838032Spetervoid
210938032Spetercataddr(pvp, evp, buf, sz, spacesub)
211038032Speter	char **pvp;
211138032Speter	char **evp;
211238032Speter	char *buf;
211338032Speter	register int sz;
211438032Speter	int spacesub;
211538032Speter{
211690792Sgshapiro	bool oatomtok = false;
211790792Sgshapiro	bool natomtok = false;
211838032Speter	register int i;
211938032Speter	register char *p;
212038032Speter
212164562Sgshapiro	if (sz <= 0)
212264562Sgshapiro		return;
212364562Sgshapiro
212438032Speter	if (spacesub == '\0')
212538032Speter		spacesub = SpaceSub;
212638032Speter
212738032Speter	if (pvp == NULL)
212838032Speter	{
212964562Sgshapiro		*buf = '\0';
213038032Speter		return;
213138032Speter	}
213238032Speter	p = buf;
213338032Speter	sz -= 2;
213490792Sgshapiro	while (*pvp != NULL && sz > 0)
213538032Speter	{
213638032Speter		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
213738032Speter		if (oatomtok && natomtok)
213864562Sgshapiro		{
213938032Speter			*p++ = spacesub;
214090792Sgshapiro			if (--sz <= 0)
214190792Sgshapiro				break;
214264562Sgshapiro		}
214390792Sgshapiro		if ((i = sm_strlcpy(p, *pvp, sz)) >= sz)
214490792Sgshapiro			break;
214538032Speter		oatomtok = natomtok;
214638032Speter		p += i;
214764562Sgshapiro		sz -= i;
214838032Speter		if (pvp++ == evp)
214938032Speter			break;
215038032Speter	}
215194334Sgshapiro#if _FFR_CATCH_LONG_STRINGS
215294334Sgshapiro	/* Don't silently truncate long strings */
215394334Sgshapiro	if (*pvp != NULL)
215494334Sgshapiro		syserr("cataddr: string too long");
215594334Sgshapiro#endif /* _FFR_CATCH_LONG_STRINGS */
215638032Speter	*p = '\0';
215738032Speter}
215890792Sgshapiro/*
215938032Speter**  SAMEADDR -- Determine if two addresses are the same
216038032Speter**
216138032Speter**	This is not just a straight comparison -- if the mailer doesn't
216238032Speter**	care about the host we just ignore it, etc.
216338032Speter**
216438032Speter**	Parameters:
216538032Speter**		a, b -- pointers to the internal forms to compare.
216638032Speter**
216738032Speter**	Returns:
216890792Sgshapiro**		true -- they represent the same mailbox.
216990792Sgshapiro**		false -- they don't.
217038032Speter**
217138032Speter**	Side Effects:
217238032Speter**		none.
217338032Speter*/
217438032Speter
217538032Speterbool
217638032Spetersameaddr(a, b)
217738032Speter	register ADDRESS *a;
217838032Speter	register ADDRESS *b;
217938032Speter{
218038032Speter	register ADDRESS *ca, *cb;
218138032Speter
218238032Speter	/* if they don't have the same mailer, forget it */
218338032Speter	if (a->q_mailer != b->q_mailer)
218490792Sgshapiro		return false;
218538032Speter
218638032Speter	/* if the user isn't the same, we can drop out */
218738032Speter	if (strcmp(a->q_user, b->q_user) != 0)
218890792Sgshapiro		return false;
218938032Speter
219038032Speter	/* if we have good uids for both but they differ, these are different */
219138032Speter	if (a->q_mailer == ProgMailer)
219238032Speter	{
219338032Speter		ca = getctladdr(a);
219438032Speter		cb = getctladdr(b);
219538032Speter		if (ca != NULL && cb != NULL &&
219638032Speter		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
219738032Speter		    ca->q_uid != cb->q_uid)
219890792Sgshapiro			return false;
219938032Speter	}
220038032Speter
220138032Speter	/* otherwise compare hosts (but be careful for NULL ptrs) */
220238032Speter	if (a->q_host == b->q_host)
220338032Speter	{
220438032Speter		/* probably both null pointers */
220590792Sgshapiro		return true;
220638032Speter	}
220738032Speter	if (a->q_host == NULL || b->q_host == NULL)
220838032Speter	{
220938032Speter		/* only one is a null pointer */
221090792Sgshapiro		return false;
221138032Speter	}
221238032Speter	if (strcmp(a->q_host, b->q_host) != 0)
221390792Sgshapiro		return false;
221438032Speter
221590792Sgshapiro	return true;
221638032Speter}
221790792Sgshapiro/*
221838032Speter**  PRINTADDR -- print address (for debugging)
221938032Speter**
222038032Speter**	Parameters:
222138032Speter**		a -- the address to print
222238032Speter**		follow -- follow the q_next chain.
222338032Speter**
222438032Speter**	Returns:
222538032Speter**		none.
222638032Speter**
222738032Speter**	Side Effects:
222838032Speter**		none.
222938032Speter*/
223038032Speter
223138032Speterstruct qflags
223238032Speter{
223390792Sgshapiro	char		*qf_name;
223490792Sgshapiro	unsigned long	qf_bit;
223538032Speter};
223638032Speter
223764562Sgshapirostatic struct qflags	AddressFlags[] =
223838032Speter{
223938032Speter	{ "QGOODUID",		QGOODUID	},
224038032Speter	{ "QPRIMARY",		QPRIMARY	},
224138032Speter	{ "QNOTREMOTE",		QNOTREMOTE	},
224238032Speter	{ "QSELFREF",		QSELFREF	},
224338032Speter	{ "QBOGUSSHELL",	QBOGUSSHELL	},
224438032Speter	{ "QUNSAFEADDR",	QUNSAFEADDR	},
224538032Speter	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
224638032Speter	{ "QPINGONFAILURE",	QPINGONFAILURE	},
224738032Speter	{ "QPINGONDELAY",	QPINGONDELAY	},
224838032Speter	{ "QHASNOTIFY",		QHASNOTIFY	},
224938032Speter	{ "QRELAYED",		QRELAYED	},
225038032Speter	{ "QEXPANDED",		QEXPANDED	},
225138032Speter	{ "QDELIVERED",		QDELIVERED	},
225238032Speter	{ "QDELAYED",		QDELAYED	},
225338032Speter	{ "QTHISPASS",		QTHISPASS	},
225438032Speter	{ "QRCPTOK",		QRCPTOK		},
225571345Sgshapiro	{ NULL,			0		}
225638032Speter};
225738032Speter
225838032Spetervoid
225938032Speterprintaddr(a, follow)
226038032Speter	register ADDRESS *a;
226138032Speter	bool follow;
226238032Speter{
226338032Speter	register MAILER *m;
226438032Speter	MAILER pseudomailer;
226538032Speter	register struct qflags *qfp;
226638032Speter	bool firstone;
226738032Speter
226838032Speter	if (a == NULL)
226938032Speter	{
227090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "[NULL]\n");
227138032Speter		return;
227238032Speter	}
227338032Speter
227438032Speter	while (a != NULL)
227538032Speter	{
227690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%p=", a);
227790792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
227838032Speter
227938032Speter		/* find the mailer -- carefully */
228038032Speter		m = a->q_mailer;
228138032Speter		if (m == NULL)
228238032Speter		{
228338032Speter			m = &pseudomailer;
228438032Speter			m->m_mno = -1;
228538032Speter			m->m_name = "NULL";
228638032Speter		}
228738032Speter
228890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
228990792Sgshapiro				     "%s:\n\tmailer %d (%s), host `%s'\n",
229090792Sgshapiro				     a->q_paddr == NULL ? "<null>" : a->q_paddr,
229190792Sgshapiro				     m->m_mno, m->m_name,
229290792Sgshapiro				     a->q_host == NULL ? "<null>" : a->q_host);
229390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
229490792Sgshapiro				     "\tuser `%s', ruser `%s'\n",
229590792Sgshapiro				     a->q_user,
229690792Sgshapiro				     a->q_ruser == NULL ? "<null>" : a->q_ruser);
229790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tstate=");
229864562Sgshapiro		switch (a->q_state)
229964562Sgshapiro		{
230064562Sgshapiro		  case QS_OK:
230190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK");
230264562Sgshapiro			break;
230364562Sgshapiro
230464562Sgshapiro		  case QS_DONTSEND:
230590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
230690792Sgshapiro					     "DONTSEND");
230764562Sgshapiro			break;
230864562Sgshapiro
230964562Sgshapiro		  case QS_BADADDR:
231090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
231190792Sgshapiro					     "BADADDR");
231264562Sgshapiro			break;
231364562Sgshapiro
231464562Sgshapiro		  case QS_QUEUEUP:
231590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
231690792Sgshapiro					     "QUEUEUP");
231764562Sgshapiro			break;
231864562Sgshapiro
231990792Sgshapiro		  case QS_RETRY:
232090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "RETRY");
232190792Sgshapiro			break;
232290792Sgshapiro
232364562Sgshapiro		  case QS_SENT:
232490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "SENT");
232564562Sgshapiro			break;
232664562Sgshapiro
232764562Sgshapiro		  case QS_VERIFIED:
232890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
232990792Sgshapiro					     "VERIFIED");
233064562Sgshapiro			break;
233164562Sgshapiro
233264562Sgshapiro		  case QS_EXPANDED:
233390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
233490792Sgshapiro					     "EXPANDED");
233564562Sgshapiro			break;
233664562Sgshapiro
233764562Sgshapiro		  case QS_SENDER:
233890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
233990792Sgshapiro					     "SENDER");
234064562Sgshapiro			break;
234164562Sgshapiro
234264562Sgshapiro		  case QS_CLONED:
234390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
234490792Sgshapiro					     "CLONED");
234564562Sgshapiro			break;
234664562Sgshapiro
234764562Sgshapiro		  case QS_DISCARDED:
234890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
234990792Sgshapiro					     "DISCARDED");
235064562Sgshapiro			break;
235164562Sgshapiro
235264562Sgshapiro		  case QS_REPLACED:
235390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
235490792Sgshapiro					     "REPLACED");
235564562Sgshapiro			break;
235664562Sgshapiro
235764562Sgshapiro		  case QS_REMOVED:
235890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
235990792Sgshapiro					     "REMOVED");
236064562Sgshapiro			break;
236164562Sgshapiro
236264562Sgshapiro		  case QS_DUPLICATE:
236390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
236490792Sgshapiro					     "DUPLICATE");
236564562Sgshapiro			break;
236664562Sgshapiro
236764562Sgshapiro		  case QS_INCLUDED:
236890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
236990792Sgshapiro					     "INCLUDED");
237064562Sgshapiro			break;
237164562Sgshapiro
237264562Sgshapiro		  default:
237390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
237490792Sgshapiro					     "%d", a->q_state);
237564562Sgshapiro			break;
237664562Sgshapiro		}
237790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
237890792Sgshapiro				     ", next=%p, alias %p, uid %d, gid %d\n",
237990792Sgshapiro				     a->q_next, a->q_alias,
238090792Sgshapiro				     (int) a->q_uid, (int) a->q_gid);
238190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tflags=%lx<",
238290792Sgshapiro				     a->q_flags);
238390792Sgshapiro		firstone = true;
238438032Speter		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
238538032Speter		{
238638032Speter			if (!bitset(qfp->qf_bit, a->q_flags))
238738032Speter				continue;
238838032Speter			if (!firstone)
238990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
239090792Sgshapiro						     ",");
239190792Sgshapiro			firstone = false;
239290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
239390792Sgshapiro					     qfp->qf_name);
239438032Speter		}
239590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n");
239690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
239790792Sgshapiro				     "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
239890792Sgshapiro				     a->q_owner == NULL ? "(none)" : a->q_owner,
239990792Sgshapiro				     a->q_home == NULL ? "(none)" : a->q_home,
240090792Sgshapiro				     a->q_fullname == NULL ? "(none)" : a->q_fullname);
240190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
240290792Sgshapiro				     "\torcpt=\"%s\", statmta=%s, status=%s\n",
240390792Sgshapiro				     a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
240490792Sgshapiro				     a->q_statmta == NULL ? "(none)" : a->q_statmta,
240590792Sgshapiro				     a->q_status == NULL ? "(none)" : a->q_status);
240690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
240790792Sgshapiro				     "\tfinalrcpt=\"%s\"\n",
240890792Sgshapiro				     a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
240990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
241090792Sgshapiro				     "\trstatus=\"%s\"\n",
241190792Sgshapiro				     a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
241290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
241390792Sgshapiro				     "\tstatdate=%s\n",
241490792Sgshapiro				     a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
241538032Speter
241638032Speter		if (!follow)
241738032Speter			return;
241838032Speter		a = a->q_next;
241938032Speter	}
242038032Speter}
242190792Sgshapiro/*
242290792Sgshapiro**  EMPTYADDR -- return true if this address is empty (``<>'')
242338032Speter**
242438032Speter**	Parameters:
242538032Speter**		a -- pointer to the address
242638032Speter**
242738032Speter**	Returns:
242890792Sgshapiro**		true -- if this address is "empty" (i.e., no one should
242938032Speter**			ever generate replies to it.
243090792Sgshapiro**		false -- if it is a "regular" (read: replyable) address.
243138032Speter*/
243238032Speter
243338032Speterbool
243438032Speteremptyaddr(a)
243538032Speter	register ADDRESS *a;
243638032Speter{
243738032Speter	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
243838032Speter	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
243938032Speter}
244090792Sgshapiro/*
244138032Speter**  REMOTENAME -- return the name relative to the current mailer
244238032Speter**
244338032Speter**	Parameters:
244438032Speter**		name -- the name to translate.
244538032Speter**		m -- the mailer that we want to do rewriting relative
244638032Speter**			to.
244738032Speter**		flags -- fine tune operations.
244838032Speter**		pstat -- pointer to status word.
244938032Speter**		e -- the current envelope.
245038032Speter**
245138032Speter**	Returns:
245238032Speter**		the text string representing this address relative to
245338032Speter**			the receiving mailer.
245438032Speter**
245538032Speter**	Side Effects:
245638032Speter**		none.
245738032Speter**
245838032Speter**	Warnings:
245938032Speter**		The text string returned is tucked away locally;
246038032Speter**			copy it if you intend to save it.
246138032Speter*/
246238032Speter
246338032Speterchar *
246438032Speterremotename(name, m, flags, pstat, e)
246538032Speter	char *name;
246638032Speter	struct mailer *m;
246738032Speter	int flags;
246838032Speter	int *pstat;
246938032Speter	register ENVELOPE *e;
247038032Speter{
247138032Speter	register char **pvp;
247290792Sgshapiro	char *SM_NONVOLATILE fancy;
247390792Sgshapiro	char *oldg;
247438032Speter	int rwset;
247538032Speter	static char buf[MAXNAME + 1];
247638032Speter	char lbuf[MAXNAME + 1];
247738032Speter	char pvpbuf[PSBUFSIZE];
247864562Sgshapiro	char addrtype[4];
247938032Speter
248038032Speter	if (tTd(12, 1))
248190792Sgshapiro		sm_dprintf("remotename(%s)\n", name);
248238032Speter
248338032Speter	/* don't do anything if we are tagging it as special */
248438032Speter	if (bitset(RF_SENDERADDR, flags))
248564562Sgshapiro	{
248638032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
248738032Speter						     : m->m_se_rwset;
248864562Sgshapiro		addrtype[2] = 's';
248964562Sgshapiro	}
249038032Speter	else
249164562Sgshapiro	{
249238032Speter		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
249338032Speter						     : m->m_re_rwset;
249464562Sgshapiro		addrtype[2] = 'r';
249564562Sgshapiro	}
249638032Speter	if (rwset < 0)
249764562Sgshapiro		return name;
249864562Sgshapiro	addrtype[1] = ' ';
249964562Sgshapiro	addrtype[3] = '\0';
250064562Sgshapiro	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
250190792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
250238032Speter
250338032Speter	/*
250438032Speter	**  Do a heuristic crack of this name to extract any comment info.
250538032Speter	**	This will leave the name as a comment and a $g macro.
250638032Speter	*/
250738032Speter
250838032Speter	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
250938032Speter		fancy = "\201g";
251038032Speter	else
251138032Speter		fancy = crackaddr(name);
251238032Speter
251338032Speter	/*
251438032Speter	**  Turn the name into canonical form.
251538032Speter	**	Normally this will be RFC 822 style, i.e., "user@domain".
251638032Speter	**	If this only resolves to "user", and the "C" flag is
251738032Speter	**	specified in the sending mailer, then the sender's
251838032Speter	**	domain will be appended.
251938032Speter	*/
252038032Speter
252138032Speter	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
252238032Speter	if (pvp == NULL)
252364562Sgshapiro		return name;
252490792Sgshapiro	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
252538032Speter		*pstat = EX_TEMPFAIL;
252638032Speter	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
252738032Speter	{
252838032Speter		/* append from domain to this address */
252938032Speter		register char **pxp = pvp;
253064562Sgshapiro		int l = MAXATOM;	/* size of buffer for pvp */
253138032Speter
253238032Speter		/* see if there is an "@domain" in the current name */
253338032Speter		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
253464562Sgshapiro		{
253538032Speter			pxp++;
253664562Sgshapiro			--l;
253764562Sgshapiro		}
253838032Speter		if (*pxp == NULL)
253938032Speter		{
254038032Speter			/* no.... append the "@domain" from the sender */
254138032Speter			register char **qxq = e->e_fromdomain;
254238032Speter
254338032Speter			while ((*pxp++ = *qxq++) != NULL)
254464562Sgshapiro			{
254564562Sgshapiro				if (--l <= 0)
254664562Sgshapiro				{
254764562Sgshapiro					*--pxp = NULL;
254864562Sgshapiro					usrerr("553 5.1.0 remotename: too many tokens");
254964562Sgshapiro					*pstat = EX_UNAVAILABLE;
255064562Sgshapiro					break;
255164562Sgshapiro				}
255264562Sgshapiro			}
255390792Sgshapiro			if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
255438032Speter				*pstat = EX_TEMPFAIL;
255538032Speter		}
255638032Speter	}
255738032Speter
255838032Speter	/*
255938032Speter	**  Do more specific rewriting.
256038032Speter	**	Rewrite using ruleset 1 or 2 depending on whether this is
256138032Speter	**		a sender address or not.
256238032Speter	**	Then run it through any receiving-mailer-specific rulesets.
256338032Speter	*/
256438032Speter
256538032Speter	if (bitset(RF_SENDERADDR, flags))
256638032Speter	{
256790792Sgshapiro		if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
256838032Speter			*pstat = EX_TEMPFAIL;
256938032Speter	}
257038032Speter	else
257138032Speter	{
257290792Sgshapiro		if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
257338032Speter			*pstat = EX_TEMPFAIL;
257438032Speter	}
257538032Speter	if (rwset > 0)
257638032Speter	{
257790792Sgshapiro		if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
257838032Speter			*pstat = EX_TEMPFAIL;
257938032Speter	}
258038032Speter
258138032Speter	/*
258238032Speter	**  Do any final sanitation the address may require.
258338032Speter	**	This will normally be used to turn internal forms
258438032Speter	**	(e.g., user@host.LOCAL) into external form.  This
258538032Speter	**	may be used as a default to the above rules.
258638032Speter	*/
258738032Speter
258890792Sgshapiro	if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
258938032Speter		*pstat = EX_TEMPFAIL;
259038032Speter
259138032Speter	/*
259238032Speter	**  Now restore the comment information we had at the beginning.
259338032Speter	*/
259438032Speter
259538032Speter	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
259690792Sgshapiro	oldg = macget(&e->e_macro, 'g');
259790792Sgshapiro	macset(&e->e_macro, 'g', lbuf);
259838032Speter
259990792Sgshapiro	SM_TRY
260090792Sgshapiro		/* need to make sure route-addrs have <angle brackets> */
260190792Sgshapiro		if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
260290792Sgshapiro			expand("<\201g>", buf, sizeof buf, e);
260390792Sgshapiro		else
260490792Sgshapiro			expand(fancy, buf, sizeof buf, e);
260590792Sgshapiro	SM_FINALLY
260690792Sgshapiro		macset(&e->e_macro, 'g', oldg);
260790792Sgshapiro	SM_END_TRY
260838032Speter
260938032Speter	if (tTd(12, 1))
261090792Sgshapiro		sm_dprintf("remotename => `%s'\n", buf);
261164562Sgshapiro	return buf;
261238032Speter}
261390792Sgshapiro/*
261438032Speter**  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
261538032Speter**
261638032Speter**	Parameters:
261738032Speter**		a -- the address to map (but just the user name part).
261838032Speter**		sendq -- the sendq in which to install any replacement
261938032Speter**			addresses.
262038032Speter**		aliaslevel -- the alias nesting depth.
262138032Speter**		e -- the envelope.
262238032Speter**
262338032Speter**	Returns:
262438032Speter**		none.
262538032Speter*/
262638032Speter
262738032Speter#define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
262838032Speter			 Q_PINGFLAGS|QHASNOTIFY|\
262990792Sgshapiro			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
263090792Sgshapiro			 QBYTRACE|QBYNDELAY|QBYNRELAY)
263138032Speter
263238032Spetervoid
263338032Spetermaplocaluser(a, sendq, aliaslevel, e)
263438032Speter	register ADDRESS *a;
263538032Speter	ADDRESS **sendq;
263638032Speter	int aliaslevel;
263738032Speter	ENVELOPE *e;
263838032Speter{
263938032Speter	register char **pvp;
264090792Sgshapiro	register ADDRESS *SM_NONVOLATILE a1 = NULL;
264138032Speter	auto char *delimptr;
264238032Speter	char pvpbuf[PSBUFSIZE];
264338032Speter
264438032Speter	if (tTd(29, 1))
264538032Speter	{
264690792Sgshapiro		sm_dprintf("maplocaluser: ");
264790792Sgshapiro		printaddr(a, false);
264838032Speter	}
264938032Speter	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
265038032Speter	if (pvp == NULL)
265138032Speter	{
265238032Speter		if (tTd(29, 9))
265390792Sgshapiro			sm_dprintf("maplocaluser: cannot prescan %s\n",
265464562Sgshapiro				a->q_user);
265538032Speter		return;
265638032Speter	}
265738032Speter
265890792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
265990792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
266090792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
266164562Sgshapiro
266290792Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
266390792Sgshapiro	if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
266438032Speter	{
266538032Speter		if (tTd(29, 9))
266690792Sgshapiro			sm_dprintf("maplocaluser: rewrite tempfail\n");
266764562Sgshapiro		a->q_state = QS_QUEUEUP;
266838032Speter		a->q_status = "4.4.3";
266938032Speter		return;
267038032Speter	}
267138032Speter	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
267238032Speter	{
267338032Speter		if (tTd(29, 9))
267490792Sgshapiro			sm_dprintf("maplocaluser: doesn't resolve\n");
267538032Speter		return;
267638032Speter	}
267738032Speter
267890792Sgshapiro	SM_TRY
267990792Sgshapiro		a1 = buildaddr(pvp, NULL, 0, e);
268090792Sgshapiro	SM_EXCEPT(exc, "E:mta.quickabort")
268190792Sgshapiro
268290792Sgshapiro		/*
268390792Sgshapiro		**  mark address as bad, S5 returned an error
268490792Sgshapiro		**	and we gave that back to the SMTP client.
268590792Sgshapiro		*/
268690792Sgshapiro
268790792Sgshapiro		a->q_state = QS_DONTSEND;
268890792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
268990792Sgshapiro	SM_END_TRY
269090792Sgshapiro
269138032Speter	/* if non-null, mailer destination specified -- has it changed? */
269238032Speter	if (a1 == NULL || sameaddr(a, a1))
269338032Speter	{
269438032Speter		if (tTd(29, 9))
269590792Sgshapiro			sm_dprintf("maplocaluser: address unchanged\n");
269638032Speter		return;
269738032Speter	}
269838032Speter
269938032Speter	/* make new address take on flags and print attributes of old */
270038032Speter	a1->q_flags &= ~Q_COPYFLAGS;
270138032Speter	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
270290792Sgshapiro	a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
270390792Sgshapiro	a1->q_finalrcpt = a->q_finalrcpt;
270464562Sgshapiro	a1->q_orcpt = a->q_orcpt;
270538032Speter
270638032Speter	/* mark old address as dead; insert new address */
270764562Sgshapiro	a->q_state = QS_REPLACED;
270838032Speter	if (tTd(29, 5))
270938032Speter	{
271090792Sgshapiro		sm_dprintf("maplocaluser: QS_REPLACED ");
271190792Sgshapiro		printaddr(a, false);
271238032Speter	}
271338032Speter	a1->q_alias = a;
271490792Sgshapiro	allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
271538032Speter	(void) recipient(a1, sendq, aliaslevel, e);
271638032Speter}
271790792Sgshapiro/*
271838032Speter**  DEQUOTE_INIT -- initialize dequote map
271938032Speter**
272038032Speter**	Parameters:
272138032Speter**		map -- the internal map structure.
272238032Speter**		args -- arguments.
272338032Speter**
272438032Speter**	Returns:
272590792Sgshapiro**		true.
272638032Speter*/
272738032Speter
272838032Speterbool
272938032Speterdequote_init(map, args)
273038032Speter	MAP *map;
273138032Speter	char *args;
273238032Speter{
273338032Speter	register char *p = args;
273438032Speter
273564562Sgshapiro	/* there is no check whether there is really an argument */
273638032Speter	map->map_mflags |= MF_KEEPQUOTES;
273738032Speter	for (;;)
273838032Speter	{
273938032Speter		while (isascii(*p) && isspace(*p))
274038032Speter			p++;
274138032Speter		if (*p != '-')
274238032Speter			break;
274338032Speter		switch (*++p)
274438032Speter		{
274538032Speter		  case 'a':
274638032Speter			map->map_app = ++p;
274738032Speter			break;
274838032Speter
274964562Sgshapiro		  case 'D':
275064562Sgshapiro			map->map_mflags |= MF_DEFER;
275164562Sgshapiro			break;
275264562Sgshapiro
275364562Sgshapiro		  case 'S':
275438032Speter		  case 's':
275564562Sgshapiro			map->map_spacesub = *++p;
275638032Speter			break;
275738032Speter		}
275838032Speter		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
275938032Speter			p++;
276038032Speter		if (*p != '\0')
276138032Speter			*p = '\0';
276238032Speter	}
276338032Speter	if (map->map_app != NULL)
276438032Speter		map->map_app = newstr(map->map_app);
276538032Speter
276690792Sgshapiro	return true;
276738032Speter}
276890792Sgshapiro/*
276938032Speter**  DEQUOTE_MAP -- unquote an address
277038032Speter**
277138032Speter**	Parameters:
277238032Speter**		map -- the internal map structure (ignored).
277338032Speter**		name -- the name to dequote.
277438032Speter**		av -- arguments (ignored).
277538032Speter**		statp -- pointer to status out-parameter.
277638032Speter**
277738032Speter**	Returns:
277838032Speter**		NULL -- if there were no quotes, or if the resulting
277938032Speter**			unquoted buffer would not be acceptable to prescan.
278038032Speter**		else -- The dequoted buffer.
278138032Speter*/
278238032Speter
278338032Speter/* ARGSUSED2 */
278438032Speterchar *
278538032Speterdequote_map(map, name, av, statp)
278638032Speter	MAP *map;
278738032Speter	char *name;
278838032Speter	char **av;
278938032Speter	int *statp;
279038032Speter{
279138032Speter	register char *p;
279238032Speter	register char *q;
279338032Speter	register char c;
279438032Speter	int anglecnt = 0;
279538032Speter	int cmntcnt = 0;
279638032Speter	int quotecnt = 0;
279738032Speter	int spacecnt = 0;
279890792Sgshapiro	bool quotemode = false;
279990792Sgshapiro	bool bslashmode = false;
280064562Sgshapiro	char spacesub = map->map_spacesub;
280138032Speter
280238032Speter	for (p = q = name; (c = *p++) != '\0'; )
280338032Speter	{
280438032Speter		if (bslashmode)
280538032Speter		{
280690792Sgshapiro			bslashmode = false;
280738032Speter			*q++ = c;
280838032Speter			continue;
280938032Speter		}
281038032Speter
281138032Speter		if (c == ' ' && spacesub != '\0')
281238032Speter			c = spacesub;
281338032Speter
281438032Speter		switch (c)
281538032Speter		{
281638032Speter		  case '\\':
281790792Sgshapiro			bslashmode = true;
281838032Speter			break;
281938032Speter
282038032Speter		  case '(':
282138032Speter			cmntcnt++;
282238032Speter			break;
282338032Speter
282438032Speter		  case ')':
282538032Speter			if (cmntcnt-- <= 0)
282638032Speter				return NULL;
282738032Speter			break;
282838032Speter
282938032Speter		  case ' ':
283064562Sgshapiro		  case '\t':
283138032Speter			spacecnt++;
283238032Speter			break;
283338032Speter		}
283438032Speter
283538032Speter		if (cmntcnt > 0)
283638032Speter		{
283738032Speter			*q++ = c;
283838032Speter			continue;
283938032Speter		}
284038032Speter
284138032Speter		switch (c)
284238032Speter		{
284338032Speter		  case '"':
284438032Speter			quotemode = !quotemode;
284538032Speter			quotecnt++;
284638032Speter			continue;
284738032Speter
284838032Speter		  case '<':
284938032Speter			anglecnt++;
285038032Speter			break;
285138032Speter
285238032Speter		  case '>':
285338032Speter			if (anglecnt-- <= 0)
285438032Speter				return NULL;
285538032Speter			break;
285638032Speter		}
285738032Speter		*q++ = c;
285838032Speter	}
285938032Speter
286038032Speter	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
286138032Speter	    quotemode || quotecnt <= 0 || spacecnt != 0)
286238032Speter		return NULL;
286338032Speter	*q++ = '\0';
286438032Speter	return map_rewrite(map, name, strlen(name), NULL);
286538032Speter}
286690792Sgshapiro/*
286738032Speter**  RSCHECK -- check string(s) for validity using rewriting sets
286838032Speter**
286938032Speter**	Parameters:
287038032Speter**		rwset -- the rewriting set to use.
287138032Speter**		p1 -- the first string to check.
287238032Speter**		p2 -- the second string to check -- may be null.
287338032Speter**		e -- the current envelope.
287464562Sgshapiro**		rmcomm -- remove comments?
287564562Sgshapiro**		cnt -- count rejections (statistics)?
287690792Sgshapiro**		logl -- logging level.
287771345Sgshapiro**		host -- NULL or relay host.
287890792Sgshapiro**		logid -- id for sm_syslog.
287938032Speter**
288038032Speter**	Returns:
288138032Speter**		EX_OK -- if the rwset doesn't resolve to $#error
288238032Speter**		else -- the failure status (message printed)
288338032Speter*/
288438032Speter
288538032Speterint
288690792Sgshapirorscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host, logid)
288738032Speter	char *rwset;
288838032Speter	char *p1;
288938032Speter	char *p2;
289038032Speter	ENVELOPE *e;
289164562Sgshapiro	bool rmcomm, cnt;
289264562Sgshapiro	int logl;
289371345Sgshapiro	char *host;
289490792Sgshapiro	char *logid;
289538032Speter{
289690792Sgshapiro	char *volatile buf;
289738032Speter	int bufsize;
289838032Speter	int saveexitstat;
289990792Sgshapiro	int volatile rstat = EX_OK;
290038032Speter	char **pvp;
290138032Speter	int rsno;
290290792Sgshapiro	bool volatile discard = false;
290338032Speter	auto ADDRESS a1;
290438032Speter	bool saveQuickAbort = QuickAbort;
290538032Speter	bool saveSuprErrs = SuprErrs;
290690792Sgshapiro#if _FFR_QUARANTINE
290790792Sgshapiro	bool quarantine = false;
290890792Sgshapiro	char ubuf[BUFSIZ * 2];
290990792Sgshapiro#endif /* _FFR_QUARANTINE */
291038032Speter	char buf0[MAXLINE];
291138032Speter	char pvpbuf[PSBUFSIZE];
291238032Speter	extern char MsgBuf[];
291338032Speter
291438032Speter	if (tTd(48, 2))
291590792Sgshapiro		sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
291638032Speter			p2 == NULL ? "(NULL)" : p2);
291738032Speter
291838032Speter	rsno = strtorwset(rwset, NULL, ST_FIND);
291938032Speter	if (rsno < 0)
292038032Speter		return EX_OK;
292138032Speter
292238032Speter	if (p2 != NULL)
292338032Speter	{
292438032Speter		bufsize = strlen(p1) + strlen(p2) + 2;
292538032Speter		if (bufsize > sizeof buf0)
292690792Sgshapiro			buf = sm_malloc_x(bufsize);
292738032Speter		else
292838032Speter		{
292938032Speter			buf = buf0;
293038032Speter			bufsize = sizeof buf0;
293138032Speter		}
293290792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
293338032Speter	}
293438032Speter	else
293538032Speter	{
293638032Speter		bufsize = strlen(p1) + 1;
293738032Speter		if (bufsize > sizeof buf0)
293890792Sgshapiro			buf = sm_malloc_x(bufsize);
293938032Speter		else
294038032Speter		{
294138032Speter			buf = buf0;
294238032Speter			bufsize = sizeof buf0;
294338032Speter		}
294490792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
294538032Speter	}
294690792Sgshapiro	SM_TRY
294738032Speter	{
294890792Sgshapiro		SuprErrs = true;
294990792Sgshapiro		QuickAbort = false;
295090792Sgshapiro		pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
295190792Sgshapiro			      rmcomm ? NULL : TokTypeNoC);
295290792Sgshapiro		SuprErrs = saveSuprErrs;
295390792Sgshapiro		if (pvp == NULL)
295490792Sgshapiro		{
295590792Sgshapiro			if (tTd(48, 2))
295690792Sgshapiro				sm_dprintf("rscheck: cannot prescan input\n");
295790792Sgshapiro	/*
295890792Sgshapiro			syserr("rscheck: cannot prescan input: \"%s\"",
295990792Sgshapiro				shortenstring(buf, MAXSHORTSTR));
296090792Sgshapiro			rstat = EX_DATAERR;
296190792Sgshapiro	*/
296290792Sgshapiro			goto finis;
296390792Sgshapiro		}
296490792Sgshapiro		(void) REWRITE(pvp, rsno, e);
296590792Sgshapiro		if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
296690792Sgshapiro		    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
296790792Sgshapiro				       strcmp(pvp[1], "discard") != 0))
296890792Sgshapiro		{
296990792Sgshapiro			goto finis;
297090792Sgshapiro		}
297166494Sgshapiro
297290792Sgshapiro		if (strcmp(pvp[1], "discard") == 0)
297390792Sgshapiro		{
297490792Sgshapiro			if (tTd(48, 2))
297590792Sgshapiro				sm_dprintf("rscheck: discard mailer selected\n");
297690792Sgshapiro			e->e_flags |= EF_DISCARD;
297790792Sgshapiro			discard = true;
297890792Sgshapiro		}
297990792Sgshapiro#if _FFR_QUARANTINE
298090792Sgshapiro		else if (strcmp(pvp[1], "error") == 0 &&
298190792Sgshapiro			 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
298290792Sgshapiro			 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
298390792Sgshapiro		{
298490792Sgshapiro			if (pvp[4] == NULL ||
298590792Sgshapiro			    (pvp[4][0] & 0377) != CANONUSER ||
298690792Sgshapiro			    pvp[5] == NULL)
298790792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
298890792Sgshapiro								 rwset);
298990792Sgshapiro			else
299090792Sgshapiro			{
299190792Sgshapiro				cataddr(&(pvp[5]), NULL, ubuf,
299290792Sgshapiro					sizeof ubuf, ' ');
299390792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
299490792Sgshapiro								 ubuf);
299590792Sgshapiro			}
299690792Sgshapiro			macdefine(&e->e_macro, A_PERM,
299790792Sgshapiro				  macid("{quarantine}"), e->e_quarmsg);
299890792Sgshapiro			quarantine = true;
299990792Sgshapiro		}
300090792Sgshapiro#endif /* _FFR_QUARANTINE */
300190792Sgshapiro		else
300290792Sgshapiro		{
300390792Sgshapiro			int savelogusrerrs = LogUsrErrs;
300490792Sgshapiro			static bool logged = false;
300590792Sgshapiro
300690792Sgshapiro			/* got an error -- process it */
300790792Sgshapiro			saveexitstat = ExitStat;
300890792Sgshapiro			LogUsrErrs = false;
300990792Sgshapiro			(void) buildaddr(pvp, &a1, 0, e);
301090792Sgshapiro			LogUsrErrs = savelogusrerrs;
301190792Sgshapiro			rstat = ExitStat;
301290792Sgshapiro			ExitStat = saveexitstat;
301390792Sgshapiro			if (!logged)
301490792Sgshapiro			{
301590792Sgshapiro				if (cnt)
301690792Sgshapiro					markstats(e, &a1, STATS_REJECT);
301790792Sgshapiro				logged = true;
301890792Sgshapiro			}
301990792Sgshapiro		}
302090792Sgshapiro
302190792Sgshapiro		if (LogLevel > logl)
302290792Sgshapiro		{
302390792Sgshapiro			char *relay;
302490792Sgshapiro			char *p;
302590792Sgshapiro			char lbuf[MAXLINE];
302690792Sgshapiro
302790792Sgshapiro			p = lbuf;
302890792Sgshapiro			if (p2 != NULL)
302990792Sgshapiro			{
303090792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
303190792Sgshapiro					", arg2=%s",
303290792Sgshapiro					p2);
303390792Sgshapiro				p += strlen(p);
303490792Sgshapiro			}
303590792Sgshapiro
303690792Sgshapiro			if (host != NULL)
303790792Sgshapiro				relay = host;
303890792Sgshapiro			else
303990792Sgshapiro				relay = macvalue('_', e);
304090792Sgshapiro			if (relay != NULL)
304190792Sgshapiro			{
304290792Sgshapiro				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
304390792Sgshapiro					", relay=%s", relay);
304490792Sgshapiro				p += strlen(p);
304590792Sgshapiro			}
304690792Sgshapiro			*p = '\0';
304790792Sgshapiro			if (discard)
304890792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
304990792Sgshapiro					  "ruleset=%s, arg1=%s%s, discard",
305090792Sgshapiro					  rwset, p1, lbuf);
305190792Sgshapiro#if _FFR_QUARANTINE
305290792Sgshapiro			else if (quarantine)
305390792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
305490792Sgshapiro					  "ruleset=%s, arg1=%s%s, quarantine=%s",
305590792Sgshapiro					  rwset, p1, lbuf, ubuf);
305690792Sgshapiro#endif /* _FFR_QUARANTINE */
305790792Sgshapiro			else
305890792Sgshapiro				sm_syslog(LOG_NOTICE, logid,
305990792Sgshapiro					  "ruleset=%s, arg1=%s%s, reject=%s",
306090792Sgshapiro					  rwset, p1, lbuf, MsgBuf);
306190792Sgshapiro		}
306290792Sgshapiro
306390792Sgshapiro	 finis: ;
306473188Sgshapiro	}
306590792Sgshapiro	SM_FINALLY
306638032Speter	{
306790792Sgshapiro		/* clean up */
306890792Sgshapiro		if (buf != buf0)
306990792Sgshapiro			sm_free(buf);
307090792Sgshapiro		QuickAbort = saveQuickAbort;
307138032Speter	}
307290792Sgshapiro	SM_END_TRY
307338032Speter
307490792Sgshapiro	setstat(rstat);
307590792Sgshapiro
307690792Sgshapiro	/* rulesets don't set errno */
307790792Sgshapiro	errno = 0;
307890792Sgshapiro	if (rstat != EX_OK && QuickAbort)
307990792Sgshapiro		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
308090792Sgshapiro	return rstat;
308190792Sgshapiro}
308290792Sgshapiro/*
308390792Sgshapiro**  RSCAP -- call rewriting set to return capabilities
308490792Sgshapiro**
308590792Sgshapiro**	Parameters:
308690792Sgshapiro**		rwset -- the rewriting set to use.
308790792Sgshapiro**		p1 -- the first string to check.
308890792Sgshapiro**		p2 -- the second string to check -- may be null.
308990792Sgshapiro**		e -- the current envelope.
309090792Sgshapiro**		pvp -- pointer to token vector.
309190792Sgshapiro**		pvpbuf -- buffer space.
309290792Sgshapiro**
309390792Sgshapiro**	Returns:
309490792Sgshapiro**		EX_UNAVAILABLE -- ruleset doesn't exist.
309590792Sgshapiro**		EX_DATAERR -- prescan() failed.
309690792Sgshapiro**		EX_OK -- rewrite() was successful.
309790792Sgshapiro**		else -- return status from rewrite().
309890792Sgshapiro*/
309990792Sgshapiro
310090792Sgshapiroint
310190792Sgshapirorscap(rwset, p1, p2, e, pvp, pvpbuf, size)
310290792Sgshapiro	char *rwset;
310390792Sgshapiro	char *p1;
310490792Sgshapiro	char *p2;
310590792Sgshapiro	ENVELOPE *e;
310690792Sgshapiro	char ***pvp;
310790792Sgshapiro	char *pvpbuf;
310890792Sgshapiro	int size;
310990792Sgshapiro{
311090792Sgshapiro	char *volatile buf;
311190792Sgshapiro	int bufsize;
311290792Sgshapiro	int volatile rstat = EX_OK;
311390792Sgshapiro	int rsno;
311490792Sgshapiro	bool saveQuickAbort = QuickAbort;
311590792Sgshapiro	bool saveSuprErrs = SuprErrs;
311690792Sgshapiro	char buf0[MAXLINE];
311790792Sgshapiro	extern char MsgBuf[];
311890792Sgshapiro
311990792Sgshapiro	if (tTd(48, 2))
312090792Sgshapiro		sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
312190792Sgshapiro			p2 == NULL ? "(NULL)" : p2);
312290792Sgshapiro
312390792Sgshapiro	if (pvp != NULL)
312490792Sgshapiro		*pvp = NULL;
312590792Sgshapiro	rsno = strtorwset(rwset, NULL, ST_FIND);
312690792Sgshapiro	if (rsno < 0)
312790792Sgshapiro		return EX_UNAVAILABLE;
312890792Sgshapiro
312990792Sgshapiro	if (p2 != NULL)
313038032Speter	{
313190792Sgshapiro		bufsize = strlen(p1) + strlen(p2) + 2;
313290792Sgshapiro		if (bufsize > sizeof buf0)
313390792Sgshapiro			buf = sm_malloc_x(bufsize);
313490792Sgshapiro		else
313590792Sgshapiro		{
313690792Sgshapiro			buf = buf0;
313790792Sgshapiro			bufsize = sizeof buf0;
313890792Sgshapiro		}
313990792Sgshapiro		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
314038032Speter	}
314164562Sgshapiro	else
314238032Speter	{
314390792Sgshapiro		bufsize = strlen(p1) + 1;
314490792Sgshapiro		if (bufsize > sizeof buf0)
314590792Sgshapiro			buf = sm_malloc_x(bufsize);
314690792Sgshapiro		else
314738032Speter		{
314890792Sgshapiro			buf = buf0;
314990792Sgshapiro			bufsize = sizeof buf0;
315038032Speter		}
315190792Sgshapiro		(void) sm_strlcpy(buf, p1, bufsize);
315238032Speter	}
315390792Sgshapiro	SM_TRY
315438032Speter	{
315590792Sgshapiro		SuprErrs = true;
315690792Sgshapiro		QuickAbort = false;
315790792Sgshapiro		*pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL);
315890792Sgshapiro		if (*pvp != NULL)
315990792Sgshapiro			rstat = REWRITE(*pvp, rsno, e);
316071345Sgshapiro		else
316138032Speter		{
316290792Sgshapiro			if (tTd(48, 2))
316390792Sgshapiro				sm_dprintf("rscap: cannot prescan input\n");
316490792Sgshapiro			rstat = EX_DATAERR;
316538032Speter		}
316638032Speter	}
316790792Sgshapiro	SM_FINALLY
316890792Sgshapiro	{
316990792Sgshapiro		/* clean up */
317090792Sgshapiro		if (buf != buf0)
317190792Sgshapiro			sm_free(buf);
317290792Sgshapiro		SuprErrs = saveSuprErrs;
317390792Sgshapiro		QuickAbort = saveQuickAbort;
317438032Speter
317590792Sgshapiro		/* prevent information leak, this may contain rewrite error */
317690792Sgshapiro		MsgBuf[0] = '\0';
317790792Sgshapiro	}
317890792Sgshapiro	SM_END_TRY
317938032Speter	return rstat;
318038032Speter}
3181