(void) REWRITE(tv, m->m_re_rwset, e); } (void) REWRITE(tv, 4, e);
(void) rewrite(tv, m->m_re_rwset, 0, e, maxatom); } (void) rewrite(tv, 4, 0, e, maxatom);
static ADDRESS	*buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));static bool	hasctrlchar __P((register char *, bool, bool));/* replacement for illegal characters in addresses */#define BAD_CHAR_REPLACEMENT	'?'/***  PARSEADDR -- Parse an address****	Parses an address and breaks it up into three parts: a**	net to transmit the message on, the host to transmit it**	to, and a user on that host.  These are loaded into an**	ADDRESS header with the values squirreled away if necessary.**	The "user" part may not be a real user; the process may**	just reoccur on that machine.  For example, on a machine**	with an arpanet connection, the address**		csvax.bill@berkeley**	will break up to a "user" of 'csvax.bill' and a host**	of 'berkeley' -- to be transmitted over the arpanet.****	Parameters:**		addr -- the address to parse.**		a -- a pointer to the address descriptor buffer.**			If NULL, an address will be created.**		flags -- describe detail for parsing.  See RF_ definitions**			in sendmail.h.**		delim -- the character to terminate the address, passed**			to prescan.**		delimptr -- if non-NULL, set to the location of the**			delim character that was found.**		e -- the envelope that will contain this address.**		isrcpt -- true if the address denotes a recipient; false**			indicates a sender.****	Returns:**		A pointer to the address descriptor header (`a' if**			`a' is non-NULL).**		NULL on error.****	Side Effects:**		e->e_to = addr*//* following delimiters are inherent to the internal algorithms */#define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */ADDRESS *parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)	char *addr;	register ADDRESS *a;	int flags;	int delim;	char **delimptr;	register ENVELOPE *e;	bool isrcpt;{	char **pvp;	auto char *delimptrbuf;	bool qup;	char pvpbuf[PSBUFSIZE];	/*	**  Initialize and prescan address.	*/	e->e_to = addr;	if (tTd(20, 1))		sm_dprintf("\n--parseaddr(%s)\n", addr);	if (delimptr == NULL)		delimptr = &delimptrbuf;	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);	if (pvp == NULL)	{		if (tTd(20, 1))			sm_dprintf("parseaddr-->NULL\n");		return NULL;	}	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))	{		if (tTd(20, 1))			sm_dprintf("parseaddr-->bad address\n");		return NULL;	}	/*	**  Save addr if we are going to have to.	**	**	We have to do this early because there is a chance that	**	the map lookups in the rewriting rules could clobber	**	static memory somewhere.	*/	if (bitset(RF_COPYPADDR, flags) && addr != NULL)	{		char savec = **delimptr;		if (savec != '\0')			**delimptr = '\0';		e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);		if (savec != '\0')			**delimptr = savec;	}	/*	**  Apply rewriting rules.	**	Ruleset 0 does basic parsing.  It must resolve.	*/	qup = false;	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)		qup = true;	if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)		qup = true;	/*	**  Build canonical address from pvp.	*/	a = buildaddr(pvp, a, flags, e);	if (hasctrlchar(a->q_user, isrcpt, true))	{		if (tTd(20, 1))			sm_dprintf("parseaddr-->bad q_user\n");		/*		**  Just mark the address as bad so DSNs work.		**  hasctrlchar() has to make sure that the address		**  has been sanitized, e.g., shortened.		*/		a->q_state = QS_BADADDR;	}	/*	**  Make local copies of the host & user and then	**  transport them out.	*/	allocaddr(a, flags, addr, e);	if (QS_IS_BADADDR(a->q_state))	{		/* weed out bad characters in the printable address too */		(void) hasctrlchar(a->q_paddr, isrcpt, false);		return a;	}	/*	**  Select a queue directory for recipient addresses.	**	This is done here and in split_across_queue_groups(),	**	but the latter applies to addresses after aliasing,	**	and only if splitting is done.	*/	if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags) &&	    OpMode != MD_INITALIAS)	{		int r;		/* call ruleset which should return a queue group name */		r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,			  sizeof(pvpbuf));		if (r == EX_OK &&		    pvp != NULL && pvp[0] != NULL &&		    (pvp[0][0] & 0377) == CANONNET &&		    pvp[1] != NULL && pvp[1][0] != '\0')		{			r = name2qid(pvp[1]);			if (r == NOQGRP && LogLevel > 10)				sm_syslog(LOG_INFO, NOQID,					"can't find queue group name %s, selection ignored",					pvp[1]);			if (tTd(20, 4) && r != NOQGRP)				sm_syslog(LOG_INFO, NOQID,					"queue group name %s -> %d",					pvp[1], r);			a->q_qgrp = r == NOQGRP ? ENVQGRP : r;		}	}	/*	**  If there was a parsing failure, mark it for queueing.	*/	if (qup && OpMode != MD_INITALIAS)	{		char *msg = "Transient parse error -- message queued for future delivery";		if (e->e_sendmode == SM_DEFER)			msg = "Deferring message until queue run";		if (tTd(20, 1))			sm_dprintf("parseaddr: queuing message\n");		message(msg);		if (e->e_message == NULL && e->e_sendmode != SM_DEFER)			e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);		a->q_state = QS_QUEUEUP;		a->q_status = "4.4.3";	}	/*	**  Compute return value.	*/	if (tTd(20, 1))	{		sm_dprintf("parseaddr-->");		printaddr(a, false);	}	return a;}/***  INVALIDADDR -- check for address containing characters used for macros****	Parameters:**		addr -- the address to check.**		delimptr -- if non-NULL: end of address to check, i.e.,**			a pointer in the address string.**		isrcpt -- true iff the address is for a recipient.****	Returns:**		true -- if the address has characters that are reservered**			for macros or is too long.**		false -- otherwise.*/boolinvalidaddr(addr, delimptr, isrcpt)	register char *addr;	char *delimptr;	bool isrcpt;{	bool result = false;	char savedelim = '\0';	char *b = addr;	int len = 0;	if (delimptr != NULL)	{		/* delimptr points to the end of the address to test */		savedelim = *delimptr;		if (savedelim != '\0')	/* if that isn't '\0' already: */			*delimptr = '\0';	/* set it */	}	for (; *addr != '\0'; addr++)	{		if ((*addr & 0340) == 0200)		{			setstat(EX_USAGE);			result = true;			*addr = BAD_CHAR_REPLACEMENT;		}		if (++len > MAXNAME - 1)		{			char saved = *addr;			*addr = '\0';			usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",			       b, MAXNAME - 1);			*addr = saved;			result = true;			goto delim;		}	}	if (result)	{		if (isrcpt)			usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"",			       b);		else			usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"",			       b);	}delim:	if (delimptr != NULL && savedelim != '\0')		*delimptr = savedelim;	/* restore old character at delimptr */	return result;}/***  HASCTRLCHAR -- check for address containing meta-characters****  Checks that the address contains no meta-characters, and contains**  no "non-printable" characters unless they are quoted or escaped.**  Quoted or escaped characters are literals.****	Parameters:**		addr -- the address to check.**		isrcpt -- true if the address is for a recipient; false**			indicates a from.**		complain -- true if an error should issued if the address**			is invalid and should be "repaired".****	Returns:**		true -- if the address has any "wierd" characters or**			non-printable characters or if a quote is unbalanced.**		false -- otherwise.*/static boolhasctrlchar(addr, isrcpt, complain)	register char *addr;	bool isrcpt, complain;{	bool quoted = false;	int len = 0;	char *result = NULL;	char *b = addr;	if (addr == NULL)		return false;	for (; *addr != '\0'; addr++)	{		if (++len > MAXNAME - 1)		{			if (complain)			{				(void) shorten_rfc822_string(b, MAXNAME - 1);				usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",				       b, MAXNAME - 1);				return true;			}			result = "too long";		}		if (!quoted && (*addr < 32 || *addr == 127))		{			result = "non-printable character";			*addr = BAD_CHAR_REPLACEMENT;			continue;		}		if (*addr == '"')			quoted = !quoted;		else if (*addr == '\\')		{			/* XXX Generic problem: no '\0' in strings. */			if (*++addr == '\0')			{				result = "trailing \\ character";				*--addr = BAD_CHAR_REPLACEMENT;				break;			}		}		if ((*addr & 0340) == 0200)		{			setstat(EX_USAGE);			result = "8-bit character";			*addr = BAD_CHAR_REPLACEMENT;			continue;		}	}	if (quoted)		result = "unbalanced quote"; /* unbalanced quote */	if (result != NULL && complain)	{		if (isrcpt)			usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)",			       b, result);		else			usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)",			       b, result);	}	return result != NULL;}/***  ALLOCADDR -- do local allocations of address on demand.****	Also lowercases the host name if requested.****	Parameters:**		a -- the address to reallocate.**		flags -- the copy flag (see RF_ definitions in sendmail.h**			for a description).**		paddr -- the printname of the address.**		e -- envelope****	Returns:**		none.****	Side Effects:**		Copies portions of a into local buffers as requested.*/static voidallocaddr(a, flags, paddr, e)	register ADDRESS *a;	int flags;	char *paddr;	ENVELOPE *e;{	if (tTd(24, 4))		sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);	a->q_paddr = paddr;	if (a->q_user == NULL)		a->q_user = "";	if (a->q_host == NULL)		a->q_host = "";	if (bitset(RF_COPYPARSE, flags))	{		a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);		if (a->q_user != a->q_paddr)			a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);	}	if (a->q_paddr == NULL)		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);	a->q_qgrp = NOAQGRP;}/***  PRESCAN -- Prescan name and make it canonical****	Scans a name and turns it into a set of tokens.  This process**	deletes blanks and comments (in parentheses) (if the token type**	for left paren is SPC).****	This routine knows about quoted strings and angle brackets.****	There are certain subtleties to this routine.  The one that**	comes to mind now is that backslashes on the ends of names**	are silently stripped off; this is intentional.  The problem**	is that some versions of sndmsg (like at LBL) set the kill**	character to something other than @ when reading addresses;**	so people type "csvax.eric\@berkeley" -- which screws up the**	berknet mailer.****	Parameters:**		addr -- the name to chomp.**		delim -- the delimiter for the address, normally**			'\0' or ','; \0 is accepted in any case.**			If '\t' then we are reading the .cf file.**		pvpbuf -- place to put the saved text -- note that**			the pointers are static.**		pvpbsize -- size of pvpbuf.**		delimptr -- if non-NULL, set to the location of the**			terminating delimiter.**		toktab -- if set, a token table to use for parsing.**			If NULL, use the default table.****	Returns:**		A pointer to a vector of tokens.**		NULL on error.*//* states and character types */#define OPR		0	/* operator */#define ATM		1	/* atom */#define QST		2	/* in quoted string */#define SPC		3	/* chewing up spaces */#define ONE		4	/* pick up one character */#define ILL		5	/* illegal character */#define NSTATES	6	/* number of states */#define TYPE		017	/* mask to select state type *//* meta bits for table */#define M		020	/* meta character; don't pass through */#define B		040	/* cause a break */#define MB		M|B	/* meta-break */static short StateTab[NSTATES][NSTATES] ={   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},};/* token type table -- it gets modified with $o characters */static unsigned char	TokTypeTab[256] ={    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,};/* token type table for MIME parsing */unsigned char	MimeTokenTab[256] ={    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,};/* token type table: don't strip comments */unsigned char	TokTypeNoC[256] ={    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,    /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,};#define NOCHAR		(-1)	/* signal nothing in lookahead token */char **prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)	char *addr;	int delim;	char pvpbuf[];	int pvpbsize;	char **delimptr;	unsigned char *toktab;{	register char *p;	register char *q;	register int c;	char **avp;	bool bslashmode;	bool route_syntax;	int cmntcnt;	int anglecnt;	char *tok;	int state;	int newstate;	char *saveto = CurEnv->e_to;	static char *av[MAXATOM + 1];	static bool firsttime = true;	extern int errno;	if (firsttime)	{		/* initialize the token type table */		char obuf[50];		firsttime = false;		if (OperatorChars == NULL)		{			if (ConfigLevel < 7)				OperatorChars = macvalue('o', CurEnv);			if (OperatorChars == NULL)				OperatorChars = ".:@[]";		}		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,		       CurEnv);		(void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf);		for (p = obuf; *p != '\0'; p++)		{			if (TokTypeTab[*p & 0xff] == ATM)				TokTypeTab[*p & 0xff] = OPR;			if (TokTypeNoC[*p & 0xff] == ATM)				TokTypeNoC[*p & 0xff] = OPR;		}	}	if (toktab == NULL)		toktab = TokTypeTab;	/* make sure error messages don't have garbage on them */	errno = 0;	q = pvpbuf;	bslashmode = false;	route_syntax = false;	cmntcnt = 0;	anglecnt = 0;	avp = av;	state = ATM;	c = NOCHAR;	p = addr;	CurEnv->e_to = p;	if (tTd(22, 11))	{		sm_dprintf("prescan: ");		xputs(p);		sm_dprintf("\n");	}	do	{		/* read a token */		tok = q;		for (;;)		{			/* store away any old lookahead character */			if (c != NOCHAR && !bslashmode)			{				/* see if there is room */				if (q >= &pvpbuf[pvpbsize - 5])				{	addrtoolong:					usrerr("553 5.1.1 Address too long");					if (strlen(addr) > MAXNAME)						addr[MAXNAME] = '\0';	returnnull:					if (delimptr != NULL)					{						if (p > addr)							p--;						*delimptr = p;					}					CurEnv->e_to = saveto;					return NULL;				}				/* squirrel it away */#if !ALLOW_255				if ((char) c == (char) -1 && !tTd(82, 101))					c &= 0x7f;#endif /* !ALLOW_255 */				*q++ = c;			}			/* read a new input character */			c = (*p++) & 0x00ff;			if (c == '\0')			{				/* diagnose and patch up bad syntax */				if (state == QST)				{					usrerr("553 Unbalanced '\"'");					c = '"';				}				else if (cmntcnt > 0)				{					usrerr("553 Unbalanced '('");					c = ')';				}				else if (anglecnt > 0)				{					c = '>';					usrerr("553 Unbalanced '<'");				}				else					break;				p--;			}			else if (c == delim && cmntcnt <= 0 && state != QST)			{				if (anglecnt <= 0)					break;				/* special case for better error management */				if (delim == ',' && !route_syntax)				{					usrerr("553 Unbalanced '<'");					c = '>';					p--;				}			}			if (tTd(22, 101))				sm_dprintf("c=%c, s=%d; ", c, state);			/* chew up special characters */			*q = '\0';			if (bslashmode)			{				bslashmode = false;				/* kludge \! for naive users */				if (cmntcnt > 0)				{					c = NOCHAR;					continue;				}				else if (c != '!' || state == QST)				{					/* see if there is room */					if (q >= &pvpbuf[pvpbsize - 5])						goto addrtoolong;					*q++ = '\\';					continue;				}			}			if (c == '\\')			{				bslashmode = true;			}			else if (state == QST)			{				/* EMPTY */				/* do nothing, just avoid next clauses */			}			else if (c == '(' && toktab['('] == SPC)			{				cmntcnt++;				c = NOCHAR;			}			else if (c == ')' && toktab['('] == SPC)			{				if (cmntcnt <= 0)				{					usrerr("553 Unbalanced ')'");					c = NOCHAR;				}				else					cmntcnt--;			}			else if (cmntcnt > 0)			{				c = NOCHAR;			}			else if (c == '<')			{				char *ptr = p;				anglecnt++;				while (isascii(*ptr) && isspace(*ptr))					ptr++;				if (*ptr == '@')					route_syntax = true;			}			else if (c == '>')			{				if (anglecnt <= 0)				{					usrerr("553 Unbalanced '>'");					c = NOCHAR;				}				else					anglecnt--;				route_syntax = false;			}			else if (delim == ' ' && isascii(c) && isspace(c))				c = ' ';			if (c == NOCHAR)				continue;			/* see if this is end of input */			if (c == delim && anglecnt <= 0 && state != QST)				break;			newstate = StateTab[state][toktab[c & 0xff]];			if (tTd(22, 101))				sm_dprintf("ns=%02o\n", newstate);			state = newstate & TYPE;			if (state == ILL)			{				if (isascii(c) && isprint(c))					usrerr("553 Illegal character %c", c);				else					usrerr("553 Illegal character 0x%02x",					       c & 0x0ff);			}			if (bitset(M, newstate))				c = NOCHAR;			if (bitset(B, newstate))				break;		}		/* new token */		if (tok != q)		{			/* see if there is room */			if (q >= &pvpbuf[pvpbsize - 5])				goto addrtoolong;			*q++ = '\0';			if (tTd(22, 36))			{				sm_dprintf("tok=");				xputs(tok);				sm_dprintf("\n");			}			if (avp >= &av[MAXATOM])			{				usrerr("553 5.1.0 prescan: too many tokens");				goto returnnull;			}			if (q - tok > MAXNAME)			{				usrerr("553 5.1.0 prescan: token too long");				goto returnnull;			}			*avp++ = tok;		}	} while (c != '\0' && (c != delim || anglecnt > 0));	*avp = NULL;	p--;	if (delimptr != NULL)		*delimptr = p;	if (tTd(22, 12))	{		sm_dprintf("prescan==>");		printav(av);	}	CurEnv->e_to = saveto;	if (av[0] == NULL)	{		if (tTd(22, 1))			sm_dprintf("prescan: null leading token\n");		return NULL;	}	return av;}/***  REWRITE -- apply rewrite rules to token vector.****	This routine is an ordered production system.  Each rewrite**	rule has a LHS (called the pattern) and a RHS (called the**	rewrite); 'rwr' points the the current rewrite rule.****	For each rewrite rule, 'avp' points the address vector we**	are trying to match against, and 'pvp' points to the pattern.**	If pvp points to a special match value (MATCHZANY, MATCHANY,**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp**	matched is saved away in the match vector (pointed to by 'mvp').****	When a match between avp & pvp does not match, we try to**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS**	we must also back out the match in mvp.  If we reach a**	MATCHANY or MATCHZANY we just extend the match and start**	over again.****	When we finally match, we rewrite the address vector**	and try over again.****	Parameters:**		pvp -- pointer to token vector.**		ruleset -- the ruleset to use for rewriting.**		reclevel -- recursion level (to catch loops).**		e -- the current envelope.**		maxatom -- maximum length of buffer (usually MAXATOM)****	Returns:**		A status code.  If EX_TEMPFAIL, higher level code should**			attempt recovery.****	Side Effects:**		pvp is modified.*/struct match{	char	**match_first;		/* first token matched */	char	**match_last;		/* last token matched */	char	**match_pattern;	/* pointer to pattern */};intrewrite(pvp, ruleset, reclevel, e, maxatom)	char **pvp;	int ruleset;	int reclevel;	register ENVELOPE *e;	int maxatom;{	register char *ap;		/* address pointer */	register char *rp;		/* rewrite pointer */	register char *rulename;	/* ruleset name */	register char *prefix;	register char **avp;		/* address vector pointer */	register char **rvp;		/* rewrite vector pointer */	register struct match *mlp;	/* cur ptr into mlist */	register struct rewrite *rwr;	/* pointer to current rewrite rule */	int ruleno;			/* current rule number */	int rstat = EX_OK;		/* return status */	int loopcount;	struct match mlist[MAXMATCH];	/* stores match on LHS */	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */	char buf[MAXLINE];	char name[6];	if (ruleset < 0 || ruleset >= MAXRWSETS)	{		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);		return EX_CONFIG;	}	rulename = RuleSetNames[ruleset];	if (rulename == NULL)	{		(void) sm_snprintf(name, sizeof name, "%d", ruleset);		rulename = name;	}	if (OpMode == MD_TEST)		prefix = "";	else		prefix = "rewrite: ruleset ";	if (OpMode == MD_TEST)	{		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,				     "%s%-16.16s   input:", prefix, rulename);		printav(pvp);	}	else if (tTd(21, 1))	{		sm_dprintf("%s%-16.16s   input:", prefix, rulename);		printav(pvp);	}	if (reclevel++ > MaxRuleRecursion)	{		syserr("rewrite: excessive recursion (max %d), ruleset %s",			MaxRuleRecursion, rulename);		return EX_CONFIG;	}	if (pvp == NULL)		return EX_USAGE;	/*	**  Run through the list of rewrite rules, applying	**	any that match.	*/	ruleno = 1;	loopcount = 0;	for (rwr = RewriteRules[ruleset]; rwr != NULL; )	{		int status;		/* if already canonical, quit now */		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)			break;		if (tTd(21, 12))		{			if (tTd(21, 15))				sm_dprintf("-----trying rule (line %d):",				       rwr->r_line);			else				sm_dprintf("-----trying rule:");			printav(rwr->r_lhs);		}		/* try to match on this rule */		mlp = mlist;		rvp = rwr->r_lhs;		avp = pvp;		if (++loopcount > 100)		{			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",				rulename, ruleno);			if (tTd(21, 1))			{				sm_dprintf("workspace: ");				printav(pvp);			}			break;		}		while ((ap = *avp) != NULL || *rvp != NULL)		{			rp = *rvp;			if (tTd(21, 35))			{				sm_dprintf("ADVANCE rp=");				xputs(rp);				sm_dprintf(", ap=");				xputs(ap);				sm_dprintf("\n");			}			if (rp == NULL)			{				/* end-of-pattern before end-of-address */				goto backup;			}			if (ap == NULL && (*rp & 0377) != MATCHZANY &&			    (*rp & 0377) != MATCHZERO)			{				/* end-of-input with patterns left */				goto backup;			}			switch (*rp & 0377)			{			  case MATCHCLASS:				/* match any phrase in a class */				mlp->match_pattern = rvp;				mlp->match_first = avp;	extendclass:				ap = *avp;				if (ap == NULL)					goto backup;				mlp->match_last = avp++;				cataddr(mlp->match_first, mlp->match_last,					buf, sizeof buf, '\0');				if (!wordinclass(buf, rp[1]))				{					if (tTd(21, 36))					{						sm_dprintf("EXTEND  rp=");						xputs(rp);						sm_dprintf(", ap=");						xputs(ap);						sm_dprintf("\n");					}					goto extendclass;				}				if (tTd(21, 36))					sm_dprintf("CLMATCH\n");				mlp++;				break;			  case MATCHNCLASS:				/* match any token not in a class */				if (wordinclass(ap, rp[1]))					goto backup;				/* FALLTHROUGH */			  case MATCHONE:			  case MATCHANY:				/* match exactly one token */				mlp->match_pattern = rvp;				mlp->match_first = avp;				mlp->match_last = avp++;				mlp++;				break;			  case MATCHZANY:				/* match zero or more tokens */				mlp->match_pattern = rvp;				mlp->match_first = avp;				mlp->match_last = avp - 1;				mlp++;				break;			  case MATCHZERO:				/* match zero tokens */				break;			  case MACRODEXPAND:				/*				**  Match against run-time macro.				**  This algorithm is broken for the				**  general case (no recursive macros,				**  improper tokenization) but should				**  work for the usual cases.				*/				ap = macvalue(rp[1], e);				mlp->match_first = avp;				if (tTd(21, 2))					sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",						macname(rp[1]),						ap == NULL ? "(NULL)" : ap);				if (ap == NULL)					break;				while (*ap != '\0')				{					if (*avp == NULL ||					    sm_strncasecmp(ap, *avp,							   strlen(*avp)) != 0)					{						/* no match */						avp = mlp->match_first;						goto backup;					}					ap += strlen(*avp++);				}				/* match */				break;			  default:				/* must have exact match */				if (sm_strcasecmp(rp, ap))					goto backup;				avp++;				break;			}			/* successful match on this token */			rvp++;			continue;	  backup:			/* match failed -- back up */			while (--mlp >= mlist)			{				rvp = mlp->match_pattern;				rp = *rvp;				avp = mlp->match_last + 1;				ap = *avp;				if (tTd(21, 36))				{					sm_dprintf("BACKUP  rp=");					xputs(rp);					sm_dprintf(", ap=");					xputs(ap);					sm_dprintf("\n");				}				if (ap == NULL)				{					/* run off the end -- back up again */					continue;				}				if ((*rp & 0377) == MATCHANY ||				    (*rp & 0377) == MATCHZANY)				{					/* extend binding and continue */					mlp->match_last = avp++;					rvp++;					mlp++;					break;				}				if ((*rp & 0377) == MATCHCLASS)				{					/* extend binding and try again */					mlp->match_last = avp;					goto extendclass;				}			}			if (mlp < mlist)			{				/* total failure to match */				break;			}		}		/*		**  See if we successfully matched		*/		if (mlp < mlist || *rvp != NULL)		{			if (tTd(21, 10))				sm_dprintf("----- rule fails\n");			rwr = rwr->r_next;			ruleno++;			loopcount = 0;			continue;		}		rvp = rwr->r_rhs;		if (tTd(21, 12))		{			sm_dprintf("-----rule matches:");			printav(rvp);		}		rp = *rvp;		if (rp != NULL)		{			if ((*rp & 0377) == CANONUSER)			{				rvp++;				rwr = rwr->r_next;				ruleno++;				loopcount = 0;			}			else if ((*rp & 0377) == CANONHOST)			{				rvp++;				rwr = NULL;			}		}		/* substitute */		for (avp = npvp; *rvp != NULL; rvp++)		{			register struct match *m;			register char **pp;			rp = *rvp;			if ((*rp & 0377) == MATCHREPL)			{				/* substitute from LHS */				m = &mlist[rp[1] - '1'];				if (m < mlist || m >= mlp)				{					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",						rulename, rp[1]);					return EX_CONFIG;				}				if (tTd(21, 15))				{					sm_dprintf("$%c:", rp[1]);					pp = m->match_first;					while (pp <= m->match_last)					{						sm_dprintf(" %p=\"", *pp);						sm_dflush();						sm_dprintf("%s\"", *pp++);					}					sm_dprintf("\n");				}				pp = m->match_first;				while (pp <= m->match_last)				{					if (avp >= &npvp[maxatom])					{						syserr("554 5.3.0 rewrite: expansion too long");						if (LogLevel > 9)							sm_syslog(LOG_ERR,								e->e_id,								"rewrite: expansion too long, ruleset=%s, ruleno=%d",								rulename,								ruleno);						return EX_DATAERR;					}					*avp++ = *pp++;				}			}			else			{				/* some sort of replacement */				if (avp >= &npvp[maxatom])				{	toolong:					syserr("554 5.3.0 rewrite: expansion too long");					if (LogLevel > 9)						sm_syslog(LOG_ERR, e->e_id,							"rewrite: expansion too long, ruleset=%s, ruleno=%d",							rulename, ruleno);					return EX_DATAERR;				}				if ((*rp & 0377) != MACRODEXPAND)				{					/* vanilla replacement */					*avp++ = rp;				}				else				{					/* $&{x} replacement */					char *mval = macvalue(rp[1], e);					char **xpvp;					int trsize = 0;					static size_t pvpb1_size = 0;					static char **pvpb1 = NULL;					char pvpbuf[PSBUFSIZE];					if (tTd(21, 2))						sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",							macname(rp[1]),							mval == NULL ? "(NULL)" : mval);					if (mval == NULL || *mval == '\0')						continue;					/* save the remainder of the input */					for (xpvp = pvp; *xpvp != NULL; xpvp++)						trsize += sizeof *xpvp;					if ((size_t) trsize > pvpb1_size)					{						if (pvpb1 != NULL)							sm_free(pvpb1);						pvpb1 = (char **)							sm_pmalloc_x(trsize);						pvpb1_size = trsize;					}					memmove((char *) pvpb1,						(char *) pvp,						trsize);					/* scan the new replacement */					xpvp = prescan(mval, '\0', pvpbuf,						       sizeof pvpbuf, NULL,						       NULL);					if (xpvp == NULL)					{						/* prescan pre-printed error */						return EX_DATAERR;					}					/* insert it into the output stream */					while (*xpvp != NULL)					{						if (tTd(21, 19))							sm_dprintf(" ... %s\n",								*xpvp);						*avp++ = sm_rpool_strdup_x(							e->e_rpool, *xpvp);						if (avp >= &npvp[maxatom])							goto toolong;						xpvp++;					}					if (tTd(21, 19))						sm_dprintf(" ... DONE\n");					/* restore the old trailing input */					memmove((char *) pvp,						(char *) pvpb1,						trsize);				}			}		}		*avp++ = NULL;		/*		**  Check for any hostname/keyword lookups.		*/		for (rvp = npvp; *rvp != NULL; rvp++)		{			char **hbrvp;			char **xpvp;			int trsize;			char *replac;			int endtoken;			STAB *map;			char *mapname;			char **key_rvp;			char **arg_rvp;			char **default_rvp;			char cbuf[MAXNAME + 1];			char *pvpb1[MAXATOM + 1];			char *argvect[10];			char pvpbuf[PSBUFSIZE];			char *nullpvp[1];			if ((**rvp & 0377) != HOSTBEGIN &&			    (**rvp & 0377) != LOOKUPBEGIN)				continue;			/*			**  Got a hostname/keyword lookup.			**			**	This could be optimized fairly easily.			*/			hbrvp = rvp;			if ((**rvp & 0377) == HOSTBEGIN)			{				endtoken = HOSTEND;				mapname = "host";			}			else			{				endtoken = LOOKUPEND;				mapname = *++rvp;			}			map = stab(mapname, ST_MAP, ST_FIND);			if (map == NULL)				syserr("554 5.3.0 rewrite: map %s not found", mapname);			/* extract the match part */			key_rvp = ++rvp;			default_rvp = NULL;			arg_rvp = argvect;			xpvp = NULL;			replac = pvpbuf;			while (*rvp != NULL && (**rvp & 0377) != endtoken)			{				int nodetype = **rvp & 0377;				if (nodetype != CANONHOST && nodetype != CANONUSER)				{					rvp++;					continue;				}				*rvp++ = NULL;				if (xpvp != NULL)				{					cataddr(xpvp, NULL, replac,						&pvpbuf[sizeof pvpbuf] - replac,						'\0');					*++arg_rvp = replac;					replac += strlen(replac) + 1;					xpvp = NULL;				}				switch (nodetype)				{				  case CANONHOST:					xpvp = rvp;					break;				  case CANONUSER:					default_rvp = rvp;					break;				}			}			if (*rvp != NULL)				*rvp++ = NULL;			if (xpvp != NULL)			{				cataddr(xpvp, NULL, replac,					&pvpbuf[sizeof pvpbuf] - replac,					'\0');				*++arg_rvp = replac;			}			*++arg_rvp = NULL;			/* save the remainder of the input string */			trsize = (int) (avp - rvp + 1) * sizeof *rvp;			memmove((char *) pvpb1, (char *) rvp, trsize);			/* look it up */			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,				map == NULL ? '\0' : map->s_map.map_spacesub);			argvect[0] = cbuf;			replac = map_lookup(map, cbuf, argvect, &rstat, e);			/* if no replacement, use default */			if (replac == NULL && default_rvp != NULL)			{				/* create the default */				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');				replac = cbuf;			}			if (replac == NULL)			{				xpvp = key_rvp;			}			else if (*replac == '\0')			{				/* null replacement */				nullpvp[0] = NULL;				xpvp = nullpvp;			}			else			{				/* scan the new replacement */				xpvp = prescan(replac, '\0', pvpbuf,					       sizeof pvpbuf, NULL, NULL);				if (xpvp == NULL)				{					/* prescan already printed error */					return EX_DATAERR;				}			}			/* append it to the token list */			for (avp = hbrvp; *xpvp != NULL; xpvp++)			{				*avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);				if (avp >= &npvp[maxatom])					goto toolong;			}			/* restore the old trailing information */			rvp = avp - 1;			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )				if (avp >= &npvp[maxatom])					goto toolong;		}		/*		**  Check for subroutine calls.		*/		status = callsubr(npvp, reclevel, e);		if (rstat == EX_OK || status == EX_TEMPFAIL)			rstat = status;		/* copy vector back into original space. */		for (avp = npvp; *avp++ != NULL;)			continue;		memmove((char *) pvp, (char *) npvp,		      (int) (avp - npvp) * sizeof *avp);		if (tTd(21, 4))		{			sm_dprintf("rewritten as:");			printav(pvp);		}	}	if (OpMode == MD_TEST)	{		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,				     "%s%-16.16s returns:", prefix, rulename);		printav(pvp);	}	else if (tTd(21, 1))	{		sm_dprintf("%s%-16.16s returns:", prefix, rulename);		printav(pvp);	}	return rstat;}/***  CALLSUBR -- call subroutines in rewrite vector****	Parameters:**		pvp -- pointer to token vector.**		reclevel -- the current recursion level.**		e -- the current envelope.****	Returns:**		The status from the subroutine call.****	Side Effects:**		pvp is modified.*/static intcallsubr(pvp, reclevel, e)	char **pvp;	int reclevel;	ENVELOPE *e;{	char **avp;	register int i;	int subr, j;	int nsubr;	int status;	int rstat = EX_OK;#define MAX_SUBR	16	int subrnumber[MAX_SUBR];	int subrindex[MAX_SUBR];	nsubr = 0;	/*	**  Look for subroutine calls in pvp, collect them into subr*[]	**  We will perform the calls in the next loop, because we will	**  call the "last" subroutine first to avoid recursive calls	**  and too much copying.	*/	for (avp = pvp, j = 0; *avp != NULL; avp++, j++)	{		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)		{			stripquotes(avp[1]);			subr = strtorwset(avp[1], NULL, ST_FIND);			if (subr < 0)			{				syserr("554 5.3.5 Unknown ruleset %s", avp[1]);				return EX_CONFIG;			}			/*			**  XXX instead of doing this we could optimize			**  the rules after reading them: just remove			**  calls to empty rulesets			*/			/* subroutine is an empty ruleset?  don't call it */			if (RewriteRules[subr] == NULL)			{				if (tTd(21, 3))					sm_dprintf("-----skip subr %s (%d)\n",						avp[1], subr);				for (i = 2; avp[i] != NULL; i++)					avp[i - 2] = avp[i];				avp[i - 2] = NULL;				continue;			}			if (++nsubr >= MAX_SUBR)			{				syserr("554 5.3.0 Too many subroutine calls (%d max)",					MAX_SUBR);				return EX_CONFIG;			}			subrnumber[nsubr] = subr;			subrindex[nsubr] = j;		}	}	/*	**  Perform the actual subroutines calls, "last" one first, i.e.,	**  go from the right to the left through all calls,	**  do the rewriting in place.	*/	for (; nsubr > 0; nsubr--)	{		subr = subrnumber[nsubr];		avp = pvp + subrindex[nsubr];		/* remove the subroutine call and name */		for (i = 2; avp[i] != NULL; i++)			avp[i - 2] = avp[i];		avp[i - 2] = NULL;		/*		**  Now we need to call the ruleset specified for		**  the subroutine. we can do this inplace since		**  we call the "last" subroutine first.		*/		status = rewrite(avp, subr, reclevel, e,				MAXATOM - subrindex[nsubr]);		if (status != EX_OK && status != EX_TEMPFAIL)			return status;		if (rstat == EX_OK || status == EX_TEMPFAIL)			rstat = status;	}	return rstat;}/***  MAP_LOOKUP -- do lookup in map****	Parameters:**		smap -- the map to use for the lookup.**		key -- the key to look up.**		argvect -- arguments to pass to the map lookup.**		pstat -- a pointer to an integer in which to store the**			status from the lookup.**		e -- the current envelope.****	Returns:**		The result of the lookup.**		NULL -- if there was no data for the given key.*/static char *map_lookup(smap, key, argvect, pstat, e)	STAB *smap;	char key[];	char **argvect;	int *pstat;	ENVELOPE *e;{	auto int status = EX_OK;	MAP *map;	char *replac;	if (smap == NULL)		return NULL;	map = &smap->s_map;	DYNOPENMAP(map);	if (e->e_sendmode == SM_DEFER &&	    bitset(MF_DEFER, map->map_mflags))	{		/* don't do any map lookups */		if (tTd(60, 1))			sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",				smap->s_name, key);		*pstat = EX_TEMPFAIL;		return NULL;	}	if (!bitset(MF_KEEPQUOTES, map->map_mflags))		stripquotes(key);	if (tTd(60, 1))	{		sm_dprintf("map_lookup(%s, %s", smap->s_name, key);		if (tTd(60, 5))		{			int i;			for (i = 0; argvect[i] != NULL; i++)				sm_dprintf(", %%%d=%s", i, argvect[i]);		}		sm_dprintf(") => ");	}	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);	if (tTd(60, 1))		sm_dprintf("%s (%d)\n",			replac != NULL ? replac : "NOT FOUND",			status);	/* should recover if status == EX_TEMPFAIL */	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))	{		*pstat = EX_TEMPFAIL;		if (tTd(60, 1))			sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",				smap->s_name, key, errno);		if (e->e_message == NULL)		{			char mbuf[320];			(void) sm_snprintf(mbuf, sizeof mbuf,				"%.80s map: lookup (%s): deferred",				smap->s_name,				shortenstring(key, MAXSHORTSTR));			e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);		}	}	if (status == EX_TEMPFAIL && map->map_tapp != NULL)	{		size_t i = strlen(key) + strlen(map->map_tapp) + 1;		static char *rwbuf = NULL;		static size_t rwbuflen = 0;		if (i > rwbuflen)		{			if (rwbuf != NULL)				sm_free(rwbuf);			rwbuflen = i;			rwbuf = (char *) sm_pmalloc_x(rwbuflen);		}		(void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);		if (tTd(60, 4))			sm_dprintf("map_lookup tempfail: returning \"%s\"\n",				rwbuf);		return rwbuf;	}	return replac;}/***  INITERRMAILERS -- initialize error and discard mailers****	Parameters:**		none.****	Returns:**		none.****	Side Effects:**		initializes error and discard mailers.*/static MAILER discardmailer;static MAILER errormailer;static char *discardargv[] = { "DISCARD", NULL };static char *errorargv[] = { "ERROR", NULL };voidiniterrmailers(){	if (discardmailer.m_name == NULL)	{		/* initialize the discard mailer */		discardmailer.m_name = "*discard*";		discardmailer.m_mailer = "DISCARD";		discardmailer.m_argv = discardargv;	}	if (errormailer.m_name == NULL)	{		/* initialize the bogus mailer */		errormailer.m_name = "*error*";		errormailer.m_mailer = "ERROR";		errormailer.m_argv = errorargv;	}}/***  BUILDADDR -- build address from token vector.****	Parameters:**		tv -- token vector.**		a -- pointer to address descriptor to fill.**			If NULL, one will be allocated.**		flags -- info regarding whether this is a sender or**			a recipient.**		e -- the current envelope.****	Returns:**		NULL if there was an error.**		'a' otherwise.****	Side Effects:**		fills in 'a'*/static struct errcodes{	char	*ec_name;		/* name of error code */	int	ec_code;		/* numeric code */} ErrorCodes[] ={	{ "usage",		EX_USAGE	},	{ "nouser",		EX_NOUSER	},	{ "nohost",		EX_NOHOST	},	{ "unavailable",	EX_UNAVAILABLE	},	{ "software",		EX_SOFTWARE	},	{ "tempfail",		EX_TEMPFAIL	},	{ "protocol",		EX_PROTOCOL	},	{ "config",		EX_CONFIG	},	{ NULL,			EX_UNAVAILABLE	}};static ADDRESS *buildaddr(tv, a, flags, e)	register char **tv;	register ADDRESS *a;	int flags;	register ENVELOPE *e;{	bool tempfail = false;	struct mailer **mp;	register struct mailer *m;	register char *p;	char *mname;	char **hostp;	char hbuf[MAXNAME + 1];	static char ubuf[MAXNAME + 2];	if (tTd(24, 5))	{		sm_dprintf("buildaddr, flags=%x, tv=", flags);		printav(tv);	}	if (a == NULL)		a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);	memset((char *) a, '\0', sizeof *a);	hbuf[0] = '\0';	/* set up default error return flags */	a->q_flags |= DefaultNotify;	/* figure out what net/mailer to use */	if (*tv == NULL || (**tv & 0377) != CANONNET)	{		syserr("554 5.3.5 buildaddr: no mailer in parsed address");badaddr:#if _FFR_ALLOW_S0_ERROR_4XX		/*		**  ExitStat may have been set by an earlier map open		**  failure (to a permanent error (EX_OSERR) in syserr())		**  so we also need to check if this particular $#error		**  return wanted a 4XX failure.		**		**  XXX the real fix is probably to set ExitStat correctly,		**  i.e., to EX_TEMPFAIL if the map open is just a temporary		**  error.		**		**  tempfail is tested here even if _FFR_ALLOW_S0_ERROR_4XX		**  is not set; that's ok because it is initialized to false.		*/#endif /* _FFR_ALLOW_S0_ERROR_4XX */		if (ExitStat == EX_TEMPFAIL || tempfail)			a->q_state = QS_QUEUEUP;		else		{			a->q_state = QS_BADADDR;			a->q_mailer = &errormailer;		}		return a;	}	mname = *++tv;	/* extract host and user portions */	if (*++tv != NULL && (**tv & 0377) == CANONHOST)		hostp = ++tv;	else		hostp = NULL;	while (*tv != NULL && (**tv & 0377) != CANONUSER)		tv++;	if (*tv == NULL)	{		syserr("554 5.3.5 buildaddr: no user");		goto badaddr;	}	if (tv == hostp)		hostp = NULL;	else if (hostp != NULL)		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');	/* save away the host name */	if (sm_strcasecmp(mname, "error") == 0)	{		/* Set up triplet for use by -bv */		a->q_mailer = &errormailer;		a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);		/* XXX wrong place? */		if (hostp != NULL)		{			register struct errcodes *ep;			a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);			if (strchr(hbuf, '.') != NULL)			{				a->q_status = sm_rpool_strdup_x(e->e_rpool,								hbuf);				setstat(dsntoexitstat(hbuf));			}			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))			{				setstat(atoi(hbuf));			}			else			{				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)						break;				setstat(ep->ec_code);			}		}		else		{			a->q_host = NULL;			setstat(EX_UNAVAILABLE);		}		stripquotes(ubuf);		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')		{			char fmt[16];			int off;			if ((off = isenhsc(ubuf + 4, ' ')) > 0)			{				ubuf[off + 4] = '\0';				off += 5;			}			else			{				off = 4;				ubuf[3] = '\0';			}			(void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s");			if (off > 4)				usrerr(fmt, ubuf + off);			else if (isenhsc(hbuf, '\0') > 0)				usrerrenh(hbuf, fmt, ubuf + off);			else				usrerr(fmt, ubuf + off);			/* XXX ubuf[off - 1] = ' '; */#if _FFR_ALLOW_S0_ERROR_4XX			if (ubuf[0] == '4')				tempfail = true;#endif /* _FFR_ALLOW_S0_ERROR_4XX */		}		else		{			usrerr("553 5.3.0 %s", ubuf);		}		goto badaddr;	}	for (mp = Mailer; (m = *mp++) != NULL; )	{		if (sm_strcasecmp(m->m_name, mname) == 0)			break;	}	if (m == NULL)	{		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);		goto badaddr;	}	a->q_mailer = m;	/* figure out what host (if any) */	if (hostp == NULL)	{		if (!bitnset(M_LOCALMAILER, m->m_flags))		{			syserr("554 5.3.5 buildaddr: no host");			goto badaddr;		}		a->q_host = NULL;	}	else		a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);	/* figure out the user */	p = ubuf;	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')	{		p++;		tv++;		a->q_flags |= QNOTREMOTE;	}	/* do special mapping for local mailer */	if (*p == '"')		p++;	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))		a->q_mailer = m = ProgMailer;	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))		a->q_mailer = m = FileMailer;	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))	{		/* may be :include: */		stripquotes(ubuf);		if (sm_strncasecmp(ubuf, ":include:", 9) == 0)		{			/* if :include:, don't need further rewriting */			a->q_mailer = m = InclMailer;			a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);			return a;		}	}	/* rewrite according recipient mailer rewriting rules */	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);	if (ConfigLevel >= 10 ||	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))	{		/* sender addresses done later */		(void) REWRITE(tv, 2, e);		if (m->m_re_rwset > 0)		       (void) REWRITE(tv, m->m_re_rwset, e);	}	(void) REWRITE(tv, 4, e);	/* save the result for the command line/RCPT argument */	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');	a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);	/*	**  Do mapping to lower case as requested by mailer	*/	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))		makelower(a->q_host);	if (!bitnset(M_USR_UPPER, m->m_flags))		makelower(a->q_user);	if (tTd(24, 6))	{		sm_dprintf("buildaddr => ");		printaddr(a, false);	}	return a;}