122976Smiriam /*
268839Seric  * Copyright (c) 1983, 1995 Eric P. Allman
363589Sbostic  * Copyright (c) 1988, 1993
463589Sbostic  *	The Regents of the University of California.  All rights reserved.
533730Sbostic  *
642828Sbostic  * %sccs.include.redist.c%
733730Sbostic  */
822976Smiriam 
922976Smiriam #ifndef lint
10*69925Seric static char sccsid[] = "@(#)parseaddr.c	8.73 (Berkeley) 06/19/95";
1133730Sbostic #endif /* not lint */
1222976Smiriam 
1356678Seric # include "sendmail.h"
14297Seric 
15297Seric /*
169888Seric **  PARSEADDR -- Parse an address
17297Seric **
18297Seric **	Parses an address and breaks it up into three parts: a
19297Seric **	net to transmit the message on, the host to transmit it
20297Seric **	to, and a user on that host.  These are loaded into an
212973Seric **	ADDRESS header with the values squirreled away if necessary.
22297Seric **	The "user" part may not be a real user; the process may
23297Seric **	just reoccur on that machine.  For example, on a machine
24297Seric **	with an arpanet connection, the address
25297Seric **		csvax.bill@berkeley
26297Seric **	will break up to a "user" of 'csvax.bill' and a host
27297Seric **	of 'berkeley' -- to be transmitted over the arpanet.
28297Seric **
29297Seric **	Parameters:
30297Seric **		addr -- the address to parse.
31297Seric **		a -- a pointer to the address descriptor buffer.
32297Seric **			If NULL, a header will be created.
3364284Seric **		flags -- describe detail for parsing.  See RF_ definitions
3464284Seric **			in sendmail.h.
3511445Seric **		delim -- the character to terminate the address, passed
3611445Seric **			to prescan.
3758333Seric **		delimptr -- if non-NULL, set to the location of the
3858333Seric **			delim character that was found.
3956678Seric **		e -- the envelope that will contain this address.
40297Seric **
41297Seric **	Returns:
42297Seric **		A pointer to the address descriptor header (`a' if
43297Seric **			`a' is non-NULL).
44297Seric **		NULL on error.
45297Seric **
46297Seric **	Side Effects:
47297Seric **		none
48297Seric */
49297Seric 
509374Seric /* following delimiters are inherent to the internal algorithms */
5159278Seric # define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */
522091Seric 
532973Seric ADDRESS *
parseaddr(addr,a,flags,delim,delimptr,e)5464284Seric parseaddr(addr, a, flags, delim, delimptr, e)
55297Seric 	char *addr;
562973Seric 	register ADDRESS *a;
5764284Seric 	int flags;
5859700Seric 	int delim;
5958333Seric 	char **delimptr;
6056678Seric 	register ENVELOPE *e;
61297Seric {
623149Seric 	register char **pvp;
6358333Seric 	auto char *delimptrbuf;
6459084Seric 	bool queueup;
6516914Seric 	char pvpbuf[PSBUFSIZE];
6656678Seric 	extern ADDRESS *buildaddr();
6757388Seric 	extern bool invalidaddr();
6869748Seric 	extern void allocaddr __P((ADDRESS *, int, char *));
69297Seric 
70297Seric 	/*
71297Seric 	**  Initialize and prescan address.
72297Seric 	*/
73297Seric 
7456678Seric 	e->e_to = addr;
757675Seric 	if (tTd(20, 1))
769888Seric 		printf("\n--parseaddr(%s)\n", addr);
773188Seric 
7858333Seric 	if (delimptr == NULL)
7958333Seric 		delimptr = &delimptrbuf;
8058333Seric 
8168711Seric 	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
823149Seric 	if (pvp == NULL)
8356729Seric 	{
8456729Seric 		if (tTd(20, 1))
8556729Seric 			printf("parseaddr-->NULL\n");
86297Seric 		return (NULL);
8756729Seric 	}
88297Seric 
8964726Seric 	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
9064726Seric 	{
9164726Seric 		if (tTd(20, 1))
9264726Seric 			printf("parseaddr-->bad address\n");
9364726Seric 		return NULL;
9464726Seric 	}
9564726Seric 
96297Seric 	/*
9764348Seric 	**  Save addr if we are going to have to.
9864348Seric 	**
9964348Seric 	**	We have to do this early because there is a chance that
10064348Seric 	**	the map lookups in the rewriting rules could clobber
10164348Seric 	**	static memory somewhere.
10264348Seric 	*/
10364348Seric 
10464348Seric 	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
10564348Seric 	{
10664348Seric 		char savec = **delimptr;
10764348Seric 
10864348Seric 		if (savec != '\0')
10964348Seric 			**delimptr = '\0';
11066794Seric 		e->e_to = addr = newstr(addr);
11164348Seric 		if (savec != '\0')
11264348Seric 			**delimptr = savec;
11364348Seric 	}
11464348Seric 
11564348Seric 	/*
1163149Seric 	**  Apply rewriting rules.
1177889Seric 	**	Ruleset 0 does basic parsing.  It must resolve.
118297Seric 	*/
119297Seric 
12059084Seric 	queueup = FALSE;
12165071Seric 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
12259084Seric 		queueup = TRUE;
12365071Seric 	if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
12459084Seric 		queueup = TRUE;
125297Seric 
126297Seric 
127297Seric 	/*
1283149Seric 	**  Build canonical address from pvp.
129297Seric 	*/
130297Seric 
13164284Seric 	a = buildaddr(pvp, a, flags, e);
132297Seric 
133297Seric 	/*
1343149Seric 	**  Make local copies of the host & user and then
1353149Seric 	**  transport them out.
136297Seric 	*/
137297Seric 
13864348Seric 	allocaddr(a, flags, addr);
13964306Seric 	if (bitset(QBADADDR, a->q_flags))
14064306Seric 		return a;
14156678Seric 
14256678Seric 	/*
14359084Seric 	**  If there was a parsing failure, mark it for queueing.
14459084Seric 	*/
14559084Seric 
14659084Seric 	if (queueup)
14759595Seric 	{
14859734Seric 		char *msg = "Transient parse error -- message queued for future delivery";
14959734Seric 
15059595Seric 		if (tTd(20, 1))
15159595Seric 			printf("parseaddr: queuing message\n");
15259734Seric 		message(msg);
15359734Seric 		if (e->e_message == NULL)
15460009Seric 			e->e_message = newstr(msg);
15559084Seric 		a->q_flags |= QQUEUEUP;
15668358Seric 		a->q_status = "4.4.3";
15759595Seric 	}
15859084Seric 
15959084Seric 	/*
16056678Seric 	**  Compute return value.
16156678Seric 	*/
16256678Seric 
16356678Seric 	if (tTd(20, 1))
1648078Seric 	{
16556678Seric 		printf("parseaddr-->");
16656678Seric 		printaddr(a, FALSE);
16756678Seric 	}
16856678Seric 
16956678Seric 	return (a);
17056678Seric }
17156678Seric /*
17257388Seric **  INVALIDADDR -- check for address containing meta-characters
17357388Seric **
17457388Seric **	Parameters:
17557388Seric **		addr -- the address to check.
17657388Seric **
17757388Seric **	Returns:
17857388Seric **		TRUE -- if the address has any "wierd" characters
17957388Seric **		FALSE -- otherwise.
18057388Seric */
18157388Seric 
18257388Seric bool
invalidaddr(addr,delimptr)18364726Seric invalidaddr(addr, delimptr)
18457388Seric 	register char *addr;
18564726Seric 	char *delimptr;
18657388Seric {
18768433Seric 	char savedelim = '\0';
18864726Seric 
18964726Seric 	if (delimptr != NULL)
19064764Seric 	{
19164726Seric 		savedelim = *delimptr;
19264764Seric 		if (savedelim != '\0')
19364764Seric 			*delimptr = '\0';
19464764Seric 	}
19564726Seric #if 0
19664726Seric 	/* for testing.... */
19764726Seric 	if (strcmp(addr, "INvalidADDR") == 0)
19857388Seric 	{
19964726Seric 		usrerr("553 INvalid ADDRess");
20064764Seric 		goto addrfailure;
20157388Seric 	}
20264726Seric #endif
20364726Seric 	for (; *addr != '\0'; addr++)
20464726Seric 	{
20564726Seric 		if ((*addr & 0340) == 0200)
20664726Seric 			break;
20764726Seric 	}
20864726Seric 	if (*addr == '\0')
20964764Seric 	{
21068400Seric 		if (delimptr != NULL && savedelim != '\0')
21164764Seric 			*delimptr = savedelim;
21264726Seric 		return FALSE;
21364764Seric 	}
21464726Seric 	setstat(EX_USAGE);
21564726Seric 	usrerr("553 Address contained invalid control characters");
21664764Seric   addrfailure:
21768446Seric 	if (delimptr != NULL && savedelim != '\0')
21864764Seric 		*delimptr = savedelim;
21964726Seric 	return TRUE;
22057388Seric }
22157388Seric /*
22256678Seric **  ALLOCADDR -- do local allocations of address on demand.
22356678Seric **
22456678Seric **	Also lowercases the host name if requested.
22556678Seric **
22656678Seric **	Parameters:
22756678Seric **		a -- the address to reallocate.
22864284Seric **		flags -- the copy flag (see RF_ definitions in sendmail.h
22964284Seric **			for a description).
23056678Seric **		paddr -- the printname of the address.
23156678Seric **
23256678Seric **	Returns:
23356678Seric **		none.
23456678Seric **
23556678Seric **	Side Effects:
23656678Seric **		Copies portions of a into local buffers as requested.
23756678Seric */
23856678Seric 
23969748Seric void
allocaddr(a,flags,paddr)24064348Seric allocaddr(a, flags, paddr)
24156678Seric 	register ADDRESS *a;
24264284Seric 	int flags;
24356678Seric 	char *paddr;
24456678Seric {
24558673Seric 	if (tTd(24, 4))
24667693Seric 		printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
24758673Seric 
24864348Seric 	a->q_paddr = paddr;
2498078Seric 
25024944Seric 	if (a->q_user == NULL)
25124944Seric 		a->q_user = "";
25224944Seric 	if (a->q_host == NULL)
25324944Seric 		a->q_host = "";
25424944Seric 
25564284Seric 	if (bitset(RF_COPYPARSE, flags))
256297Seric 	{
25724944Seric 		a->q_host = newstr(a->q_host);
2583149Seric 		if (a->q_user != a->q_paddr)
2593149Seric 			a->q_user = newstr(a->q_user);
260297Seric 	}
261297Seric 
26256678Seric 	if (a->q_paddr == NULL)
26356678Seric 		a->q_paddr = a->q_user;
264297Seric }
265297Seric /*
266297Seric **  PRESCAN -- Prescan name and make it canonical
267297Seric **
2689374Seric **	Scans a name and turns it into a set of tokens.  This process
2699374Seric **	deletes blanks and comments (in parentheses).
270297Seric **
271297Seric **	This routine knows about quoted strings and angle brackets.
272297Seric **
273297Seric **	There are certain subtleties to this routine.  The one that
274297Seric **	comes to mind now is that backslashes on the ends of names
275297Seric **	are silently stripped off; this is intentional.  The problem
276297Seric **	is that some versions of sndmsg (like at LBL) set the kill
277297Seric **	character to something other than @ when reading addresses;
278297Seric **	so people type "csvax.eric\@berkeley" -- which screws up the
279297Seric **	berknet mailer.
280297Seric **
281297Seric **	Parameters:
282297Seric **		addr -- the name to chomp.
283297Seric **		delim -- the delimiter for the address, normally
284297Seric **			'\0' or ','; \0 is accepted in any case.
28515284Seric **			If '\t' then we are reading the .cf file.
28616914Seric **		pvpbuf -- place to put the saved text -- note that
28716914Seric **			the pointers are static.
28865066Seric **		pvpbsize -- size of pvpbuf.
28958333Seric **		delimptr -- if non-NULL, set to the location of the
29058333Seric **			terminating delimiter.
29168711Seric **		toktab -- if set, a token table to use for parsing.
29268711Seric **			If NULL, use the default table.
293297Seric **
294297Seric **	Returns:
2953149Seric **		A pointer to a vector of tokens.
296297Seric **		NULL on error.
297297Seric */
298297Seric 
2998078Seric /* states and character types */
3008078Seric # define OPR		0	/* operator */
3018078Seric # define ATM		1	/* atom */
3028078Seric # define QST		2	/* in quoted string */
3038078Seric # define SPC		3	/* chewing up spaces */
3048078Seric # define ONE		4	/* pick up one character */
30568711Seric # define ILL		5	/* illegal character */
3063149Seric 
30768711Seric # define NSTATES	6	/* number of states */
3088078Seric # define TYPE		017	/* mask to select state type */
3098078Seric 
3108078Seric /* meta bits for table */
3118078Seric # define M		020	/* meta character; don't pass through */
3128078Seric # define B		040	/* cause a break */
3138078Seric # define MB		M|B	/* meta-break */
3148078Seric 
3158078Seric static short StateTab[NSTATES][NSTATES] =
3168078Seric {
31768711Seric    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
31868711Seric 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB,
31968711Seric 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB,
32068711Seric 	/*QST*/		QST,	QST,	OPR,	QST,	QST,	QST,
32168711Seric 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB,
32268711Seric 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB,
32368711Seric 	/*ILL*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M,
3248078Seric };
3258078Seric 
32665092Seric /* token type table -- it gets modified with $o characters */
32769748Seric static u_char	TokTypeTab[256] =
32865092Seric {
32968711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
33068711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
33168711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
33268711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
33368711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
33468711Seric 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
33568711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
33668711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
33768711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
33868711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
33968711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
34068711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
34168711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
34268711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
34368711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
34468711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
34568711Seric 
34668711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
34768711Seric 	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
34868711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
34968711Seric 	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
35068711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
35168711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
35268711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
35368711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
35468711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
35568711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
35668711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
35768711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
35868711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
35968711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
36068711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
36168711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
36265092Seric };
36365092Seric 
36468711Seric /* token type table for MIME parsing */
36569748Seric u_char	MimeTokenTab[256] =
36668711Seric {
36768711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
36868711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
36968711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
37068711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
37168711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
37268711Seric 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
37368711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
37468711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
37568711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
37668711Seric 	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
37768711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
37868711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
37968711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
38068711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
38168711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
38268711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
38365092Seric 
38468711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
38568711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
38668711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
38768711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
38868711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
38968711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
39068711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
39168711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
39268711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
39368711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
39468711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
39568711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
39668711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
39768711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
39868711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
39968711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
40068711Seric };
40165092Seric 
40268711Seric 
4038078Seric # define NOCHAR		-1	/* signal nothing in lookahead token */
4048078Seric 
4053149Seric char **
prescan(addr,delim,pvpbuf,pvpbsize,delimptr,toktab)40668711Seric prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
407297Seric 	char *addr;
40868353Seric 	int delim;
40916914Seric 	char pvpbuf[];
41058333Seric 	char **delimptr;
41169748Seric 	u_char *toktab;
412297Seric {
413297Seric 	register char *p;
4148078Seric 	register char *q;
4159346Seric 	register int c;
4163149Seric 	char **avp;
417297Seric 	bool bslashmode;
418297Seric 	int cmntcnt;
4198423Seric 	int anglecnt;
4203149Seric 	char *tok;
4218078Seric 	int state;
4228078Seric 	int newstate;
42359747Seric 	char *saveto = CurEnv->e_to;
4248078Seric 	static char *av[MAXATOM+1];
42565092Seric 	static char firsttime = TRUE;
42656678Seric 	extern int errno;
427297Seric 
42865092Seric 	if (firsttime)
42965092Seric 	{
43065092Seric 		/* initialize the token type table */
43165092Seric 		char obuf[50];
43265092Seric 
43365092Seric 		firsttime = FALSE;
43468529Seric 		expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
43565092Seric 		strcat(obuf, DELIMCHARS);
43665092Seric 		for (p = obuf; *p != '\0'; p++)
43765092Seric 		{
43865092Seric 			if (TokTypeTab[*p & 0xff] == ATM)
43965092Seric 				TokTypeTab[*p & 0xff] = OPR;
44065092Seric 		}
44165092Seric 	}
44268711Seric 	if (toktab == NULL)
44368711Seric 		toktab = TokTypeTab;
44465092Seric 
44515253Seric 	/* make sure error messages don't have garbage on them */
44615253Seric 	errno = 0;
44715253Seric 
44816914Seric 	q = pvpbuf;
4493149Seric 	bslashmode = FALSE;
4507800Seric 	cmntcnt = 0;
4518423Seric 	anglecnt = 0;
4523149Seric 	avp = av;
45356678Seric 	state = ATM;
4548078Seric 	c = NOCHAR;
4558078Seric 	p = addr;
45659747Seric 	CurEnv->e_to = p;
45756764Seric 	if (tTd(22, 11))
458297Seric 	{
4598078Seric 		printf("prescan: ");
4608078Seric 		xputs(p);
46123109Seric 		(void) putchar('\n');
4628078Seric 	}
4638078Seric 
4648078Seric 	do
4658078Seric 	{
4663149Seric 		/* read a token */
4673149Seric 		tok = q;
4688078Seric 		for (;;)
469297Seric 		{
4708078Seric 			/* store away any old lookahead character */
47159277Seric 			if (c != NOCHAR && !bslashmode)
4728078Seric 			{
47315284Seric 				/* see if there is room */
47465066Seric 				if (q >= &pvpbuf[pvpbsize - 5])
4758078Seric 				{
47658151Seric 					usrerr("553 Address too long");
47768526Seric 					if (strlen(addr) > MAXNAME)
47868526Seric 						addr[MAXNAME] = '\0';
47965015Seric 	returnnull:
48058333Seric 					if (delimptr != NULL)
48158333Seric 						*delimptr = p;
48259747Seric 					CurEnv->e_to = saveto;
4838078Seric 					return (NULL);
4848078Seric 				}
48515284Seric 
48615284Seric 				/* squirrel it away */
4878078Seric 				*q++ = c;
4888078Seric 			}
4898078Seric 
4908078Seric 			/* read a new input character */
4918078Seric 			c = *p++;
49257631Seric 			if (c == '\0')
49356764Seric 			{
49456764Seric 				/* diagnose and patch up bad syntax */
49556764Seric 				if (state == QST)
49656764Seric 				{
49764767Seric 					usrerr("653 Unbalanced '\"'");
49856764Seric 					c = '"';
49956764Seric 				}
50056764Seric 				else if (cmntcnt > 0)
50156764Seric 				{
50264767Seric 					usrerr("653 Unbalanced '('");
50356764Seric 					c = ')';
50456764Seric 				}
50556764Seric 				else if (anglecnt > 0)
50656764Seric 				{
50756764Seric 					c = '>';
50864767Seric 					usrerr("653 Unbalanced '<'");
50956764Seric 				}
51056764Seric 				else
51156764Seric 					break;
51215284Seric 
51356764Seric 				p--;
51456764Seric 			}
51557631Seric 			else if (c == delim && anglecnt <= 0 &&
51657631Seric 					cmntcnt <= 0 && state != QST)
51757631Seric 				break;
51856764Seric 
5198078Seric 			if (tTd(22, 101))
5208078Seric 				printf("c=%c, s=%d; ", c, state);
5218078Seric 
5223149Seric 			/* chew up special characters */
5233149Seric 			*q = '\0';
5243149Seric 			if (bslashmode)
5253149Seric 			{
52659105Seric 				bslashmode = FALSE;
52759105Seric 
52824944Seric 				/* kludge \! for naive users */
52958061Seric 				if (cmntcnt > 0)
53059105Seric 				{
53158061Seric 					c = NOCHAR;
53259105Seric 					continue;
53359105Seric 				}
53459105Seric 				else if (c != '!' || state == QST)
53559105Seric 				{
53656678Seric 					*q++ = '\\';
53759105Seric 					continue;
53859105Seric 				}
5393149Seric 			}
54056678Seric 
54156678Seric 			if (c == '\\')
5423149Seric 			{
5433149Seric 				bslashmode = TRUE;
5443149Seric 			}
54556678Seric 			else if (state == QST)
5468514Seric 			{
5478514Seric 				/* do nothing, just avoid next clauses */
5488514Seric 			}
5498078Seric 			else if (c == '(')
5504100Seric 			{
5518078Seric 				cmntcnt++;
5528078Seric 				c = NOCHAR;
5534100Seric 			}
5548078Seric 			else if (c == ')')
5553149Seric 			{
5568078Seric 				if (cmntcnt <= 0)
5573149Seric 				{
55864767Seric 					usrerr("653 Unbalanced ')'");
55963844Seric 					c = NOCHAR;
5603149Seric 				}
5618078Seric 				else
5628078Seric 					cmntcnt--;
5638078Seric 			}
5648078Seric 			else if (cmntcnt > 0)
5658078Seric 				c = NOCHAR;
5668423Seric 			else if (c == '<')
5678423Seric 				anglecnt++;
5688423Seric 			else if (c == '>')
5698423Seric 			{
5708423Seric 				if (anglecnt <= 0)
5718423Seric 				{
57264767Seric 					usrerr("653 Unbalanced '>'");
57363844Seric 					c = NOCHAR;
5748423Seric 				}
57563844Seric 				else
57663844Seric 					anglecnt--;
5778423Seric 			}
57858050Seric 			else if (delim == ' ' && isascii(c) && isspace(c))
57911423Seric 				c = ' ';
5803149Seric 
5818078Seric 			if (c == NOCHAR)
5828078Seric 				continue;
5833149Seric 
5848078Seric 			/* see if this is end of input */
58511405Seric 			if (c == delim && anglecnt <= 0 && state != QST)
5863149Seric 				break;
5873149Seric 
58868711Seric 			newstate = StateTab[state][toktab[c & 0xff]];
5898078Seric 			if (tTd(22, 101))
5908078Seric 				printf("ns=%02o\n", newstate);
5918078Seric 			state = newstate & TYPE;
59268711Seric 			if (state == ILL)
59368711Seric 			{
59468711Seric 				if (isascii(c) && isprint(c))
59568711Seric 					usrerr("653 Illegal character %c", c);
59668711Seric 				else
59768711Seric 					usrerr("653 Illegal character 0x%02x", c);
59868711Seric 			}
5998078Seric 			if (bitset(M, newstate))
6008078Seric 				c = NOCHAR;
6018078Seric 			if (bitset(B, newstate))
6024228Seric 				break;
603297Seric 		}
6043149Seric 
6053149Seric 		/* new token */
6068078Seric 		if (tok != q)
6071378Seric 		{
6088078Seric 			*q++ = '\0';
6098078Seric 			if (tTd(22, 36))
610297Seric 			{
6118078Seric 				printf("tok=");
6128078Seric 				xputs(tok);
61323109Seric 				(void) putchar('\n');
614297Seric 			}
6158078Seric 			if (avp >= &av[MAXATOM])
616297Seric 			{
61758151Seric 				syserr("553 prescan: too many tokens");
61865015Seric 				goto returnnull;
619297Seric 			}
62065015Seric 			if (q - tok > MAXNAME)
62165015Seric 			{
62265015Seric 				syserr("553 prescan: token too long");
62365015Seric 				goto returnnull;
62465015Seric 			}
6258078Seric 			*avp++ = tok;
626297Seric 		}
6278423Seric 	} while (c != '\0' && (c != delim || anglecnt > 0));
6283149Seric 	*avp = NULL;
62958333Seric 	p--;
63058333Seric 	if (delimptr != NULL)
63158333Seric 		*delimptr = p;
63256764Seric 	if (tTd(22, 12))
63356764Seric 	{
63456764Seric 		printf("prescan==>");
63556764Seric 		printav(av);
63656764Seric 	}
63759747Seric 	CurEnv->e_to = saveto;
63858546Seric 	if (av[0] == NULL)
63964966Seric 	{
64064966Seric 		if (tTd(22, 1))
64164966Seric 			printf("prescan: null leading token\n");
64258546Seric 		return (NULL);
64364966Seric 	}
64458403Seric 	return (av);
6453149Seric }
6463149Seric /*
6473149Seric **  REWRITE -- apply rewrite rules to token vector.
6483149Seric **
6494476Seric **	This routine is an ordered production system.  Each rewrite
6504476Seric **	rule has a LHS (called the pattern) and a RHS (called the
6514476Seric **	rewrite); 'rwr' points the the current rewrite rule.
6524476Seric **
6534476Seric **	For each rewrite rule, 'avp' points the address vector we
6544476Seric **	are trying to match against, and 'pvp' points to the pattern.
6558058Seric **	If pvp points to a special match value (MATCHZANY, MATCHANY,
6569585Seric **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
6579585Seric **	matched is saved away in the match vector (pointed to by 'mvp').
6584476Seric **
6594476Seric **	When a match between avp & pvp does not match, we try to
6609585Seric **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
6614476Seric **	we must also back out the match in mvp.  If we reach a
6628058Seric **	MATCHANY or MATCHZANY we just extend the match and start
6638058Seric **	over again.
6644476Seric **
6654476Seric **	When we finally match, we rewrite the address vector
6664476Seric **	and try over again.
6674476Seric **
6683149Seric **	Parameters:
6693149Seric **		pvp -- pointer to token vector.
67059027Seric **		ruleset -- the ruleset to use for rewriting.
67165071Seric **		reclevel -- recursion level (to catch loops).
67259027Seric **		e -- the current envelope.
6733149Seric **
6743149Seric **	Returns:
67559084Seric **		A status code.  If EX_TEMPFAIL, higher level code should
67659084Seric **			attempt recovery.
6773149Seric **
6783149Seric **	Side Effects:
6793149Seric **		pvp is modified.
6803149Seric */
6812091Seric 
6823149Seric struct match
6833149Seric {
6844468Seric 	char	**first;	/* first token matched */
6854468Seric 	char	**last;		/* last token matched */
68658825Seric 	char	**pattern;	/* pointer to pattern */
6873149Seric };
6883149Seric 
6894468Seric # define MAXMATCH	9	/* max params per rewrite */
6903149Seric 
69165136Seric # ifndef MAXRULERECURSION
69265136Seric #  define MAXRULERECURSION	50	/* max recursion depth */
69365136Seric # endif
6943149Seric 
69565136Seric 
69659084Seric int
rewrite(pvp,ruleset,reclevel,e)69765071Seric rewrite(pvp, ruleset, reclevel, e)
6983149Seric 	char **pvp;
6994070Seric 	int ruleset;
70065071Seric 	int reclevel;
70159027Seric 	register ENVELOPE *e;
7023149Seric {
7033149Seric 	register char *ap;		/* address pointer */
7043149Seric 	register char *rp;		/* rewrite pointer */
7053149Seric 	register char **avp;		/* address vector pointer */
7063149Seric 	register char **rvp;		/* rewrite vector pointer */
7078058Seric 	register struct match *mlp;	/* cur ptr into mlist */
7088058Seric 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
70958866Seric 	int ruleno;			/* current rule number */
71059084Seric 	int rstat = EX_OK;		/* return status */
71164740Seric 	int loopcount;
71256678Seric 	struct match mlist[MAXMATCH];	/* stores match on LHS */
7133149Seric 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
7143149Seric 
71567262Seric 	if (OpMode == MD_TEST || tTd(21, 1))
7163149Seric 	{
7178959Seric 		printf("rewrite: ruleset %2d   input:", ruleset);
71856678Seric 		printav(pvp);
7193149Seric 	}
72056678Seric 	if (ruleset < 0 || ruleset >= MAXRWSETS)
72156326Seric 	{
72258151Seric 		syserr("554 rewrite: illegal ruleset number %d", ruleset);
72359084Seric 		return EX_CONFIG;
72456326Seric 	}
72565136Seric 	if (reclevel++ > MAXRULERECURSION)
72665071Seric 	{
72765071Seric 		syserr("rewrite: infinite recursion, ruleset %d", ruleset);
72865071Seric 		return EX_CONFIG;
72965071Seric 	}
73056678Seric 	if (pvp == NULL)
73159084Seric 		return EX_USAGE;
73256326Seric 
7333149Seric 	/*
73456678Seric 	**  Run through the list of rewrite rules, applying
73556678Seric 	**	any that match.
7363149Seric 	*/
7373149Seric 
73858866Seric 	ruleno = 1;
73964740Seric 	loopcount = 0;
7404070Seric 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
7413149Seric 	{
7427675Seric 		if (tTd(21, 12))
743297Seric 		{
7448069Seric 			printf("-----trying rule:");
74556678Seric 			printav(rwr->r_lhs);
7463149Seric 		}
7473149Seric 
7483149Seric 		/* try to match on this rule */
7494468Seric 		mlp = mlist;
7508058Seric 		rvp = rwr->r_lhs;
7518058Seric 		avp = pvp;
75258866Seric 		if (++loopcount > 100)
7533149Seric 		{
75458866Seric 			syserr("554 Infinite loop in ruleset %d, rule %d",
75558866Seric 				ruleset, ruleno);
75658866Seric 			if (tTd(21, 1))
75752637Seric 			{
75856678Seric 				printf("workspace: ");
75956678Seric 				printav(pvp);
76052637Seric 			}
76158866Seric 			break;
76258866Seric 		}
76358866Seric 
76458866Seric 		while ((ap = *avp) != NULL || *rvp != NULL)
76558866Seric 		{
7663149Seric 			rp = *rvp;
7678058Seric 			if (tTd(21, 35))
7688058Seric 			{
76958825Seric 				printf("ADVANCE rp=");
77057531Seric 				xputs(rp);
77157532Seric 				printf(", ap=");
7728058Seric 				xputs(ap);
7738069Seric 				printf("\n");
7748058Seric 			}
77556678Seric 			if (rp == NULL)
77656326Seric 			{
7773149Seric 				/* end-of-pattern before end-of-address */
7788058Seric 				goto backup;
77956678Seric 			}
78058173Seric 			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
78158827Seric 			    (*rp & 0377) != MATCHZERO)
78256326Seric 			{
78358825Seric 				/* end-of-input with patterns left */
78458825Seric 				goto backup;
785297Seric 			}
78656326Seric 
78758050Seric 			switch (*rp & 0377)
7888058Seric 			{
78958814Seric 				char buf[MAXLINE];
79056326Seric 
79156678Seric 			  case MATCHCLASS:
79258825Seric 				/* match any phrase in a class */
79358825Seric 				mlp->pattern = rvp;
79458814Seric 				mlp->first = avp;
79558814Seric 	extendclass:
79658825Seric 				ap = *avp;
79758825Seric 				if (ap == NULL)
79858814Seric 					goto backup;
79958814Seric 				mlp->last = avp++;
80058814Seric 				cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
80168199Seric 				if (!wordinclass(buf, rp[1]))
80256326Seric 				{
80358825Seric 					if (tTd(21, 36))
80458825Seric 					{
80558825Seric 						printf("EXTEND  rp=");
80658825Seric 						xputs(rp);
80758825Seric 						printf(", ap=");
80858825Seric 						xputs(ap);
80958825Seric 						printf("\n");
81058825Seric 					}
81158825Seric 					goto extendclass;
81256326Seric 				}
81358825Seric 				if (tTd(21, 36))
81458825Seric 					printf("CLMATCH\n");
81558814Seric 				mlp++;
81658814Seric 				break;
8174060Seric 
81858825Seric 			  case MATCHNCLASS:
81958825Seric 				/* match any token not in a class */
82068199Seric 				if (wordinclass(ap, rp[1]))
82158825Seric 					goto backup;
82258825Seric 
82358825Seric 				/* fall through */
82458825Seric 
82556678Seric 			  case MATCHONE:
82656678Seric 			  case MATCHANY:
82756678Seric 				/* match exactly one token */
82858825Seric 				mlp->pattern = rvp;
82956678Seric 				mlp->first = avp;
83056678Seric 				mlp->last = avp++;
8318058Seric 				mlp++;
83256678Seric 				break;
8338058Seric 
83456678Seric 			  case MATCHZANY:
83556678Seric 				/* match zero or more tokens */
83658825Seric 				mlp->pattern = rvp;
83756678Seric 				mlp->first = avp;
83856678Seric 				mlp->last = avp - 1;
83956678Seric 				mlp++;
84056678Seric 				break;
84156326Seric 
84258827Seric 			  case MATCHZERO:
84358173Seric 				/* match zero tokens */
84458173Seric 				break;
84558173Seric 
84659027Seric 			  case MACRODEXPAND:
84759027Seric 				/*
84859027Seric 				**  Match against run-time macro.
84959027Seric 				**  This algorithm is broken for the
85059027Seric 				**  general case (no recursive macros,
85159027Seric 				**  improper tokenization) but should
85259027Seric 				**  work for the usual cases.
85359027Seric 				*/
85459027Seric 
85559027Seric 				ap = macvalue(rp[1], e);
85659027Seric 				mlp->first = avp;
85759027Seric 				if (tTd(21, 2))
85859027Seric 					printf("rewrite: LHS $&%c => \"%s\"\n",
85959027Seric 						rp[1],
86059027Seric 						ap == NULL ? "(NULL)" : ap);
86159027Seric 
86259027Seric 				if (ap == NULL)
86359027Seric 					break;
86460502Seric 				while (*ap != '\0')
86559027Seric 				{
86659027Seric 					if (*avp == NULL ||
86759027Seric 					    strncasecmp(ap, *avp, strlen(*avp)) != 0)
86859027Seric 					{
86959027Seric 						/* no match */
87059027Seric 						avp = mlp->first;
87159027Seric 						goto backup;
87259027Seric 					}
87359027Seric 					ap += strlen(*avp++);
87459027Seric 				}
87559027Seric 
87659027Seric 				/* match */
87759027Seric 				break;
87859027Seric 
87956678Seric 			  default:
88056678Seric 				/* must have exact match */
88156678Seric 				if (strcasecmp(rp, ap))
8828058Seric 					goto backup;
8834468Seric 				avp++;
88456678Seric 				break;
8853149Seric 			}
8863149Seric 
88756678Seric 			/* successful match on this token */
8883149Seric 			rvp++;
8893149Seric 			continue;
8903149Seric 
89158825Seric 	  backup:
89256678Seric 			/* match failed -- back up */
89358825Seric 			while (--mlp >= mlist)
8943149Seric 			{
89558825Seric 				rvp = mlp->pattern;
89656678Seric 				rp = *rvp;
89758825Seric 				avp = mlp->last + 1;
89858825Seric 				ap = *avp;
89958825Seric 
90058825Seric 				if (tTd(21, 36))
90158825Seric 				{
90258825Seric 					printf("BACKUP  rp=");
90358825Seric 					xputs(rp);
90458825Seric 					printf(", ap=");
90558825Seric 					xputs(ap);
90658825Seric 					printf("\n");
90758825Seric 				}
90858825Seric 
90958825Seric 				if (ap == NULL)
91058825Seric 				{
91158825Seric 					/* run off the end -- back up again */
91258825Seric 					continue;
91358825Seric 				}
91458050Seric 				if ((*rp & 0377) == MATCHANY ||
91558050Seric 				    (*rp & 0377) == MATCHZANY)
9164468Seric 				{
91756678Seric 					/* extend binding and continue */
91858825Seric 					mlp->last = avp++;
91956678Seric 					rvp++;
92058825Seric 					mlp++;
92156678Seric 					break;
9224468Seric 				}
92358825Seric 				if ((*rp & 0377) == MATCHCLASS)
92456678Seric 				{
92558814Seric 					/* extend binding and try again */
92663397Seric 					mlp->last = avp;
92758814Seric 					goto extendclass;
92858814Seric 				}
9293149Seric 			}
9303149Seric 
93158825Seric 			if (mlp < mlist)
93256678Seric 			{
93356678Seric 				/* total failure to match */
93456326Seric 				break;
9353149Seric 			}
936297Seric 		}
9373149Seric 
9383149Seric 		/*
93956678Seric 		**  See if we successfully matched
9403149Seric 		*/
9413149Seric 
94258827Seric 		if (mlp < mlist || *rvp != NULL)
9433149Seric 		{
9449374Seric 			if (tTd(21, 10))
9459374Seric 				printf("----- rule fails\n");
9469374Seric 			rwr = rwr->r_next;
94758866Seric 			ruleno++;
94864740Seric 			loopcount = 0;
9499374Seric 			continue;
9509374Seric 		}
9513149Seric 
9529374Seric 		rvp = rwr->r_rhs;
9539374Seric 		if (tTd(21, 12))
9549374Seric 		{
9559374Seric 			printf("-----rule matches:");
95656678Seric 			printav(rvp);
9579374Seric 		}
9589374Seric 
9599374Seric 		rp = *rvp;
96058050Seric 		if ((*rp & 0377) == CANONUSER)
9619374Seric 		{
9629374Seric 			rvp++;
9639374Seric 			rwr = rwr->r_next;
96458866Seric 			ruleno++;
96564740Seric 			loopcount = 0;
9669374Seric 		}
96758050Seric 		else if ((*rp & 0377) == CANONHOST)
9689374Seric 		{
9699374Seric 			rvp++;
9709374Seric 			rwr = NULL;
9719374Seric 		}
97258050Seric 		else if ((*rp & 0377) == CANONNET)
9739374Seric 			rwr = NULL;
9749374Seric 
9759374Seric 		/* substitute */
9769374Seric 		for (avp = npvp; *rvp != NULL; rvp++)
9779374Seric 		{
9789374Seric 			register struct match *m;
9799374Seric 			register char **pp;
9809374Seric 
9818058Seric 			rp = *rvp;
98258050Seric 			if ((*rp & 0377) == MATCHREPL)
9838058Seric 			{
98416914Seric 				/* substitute from LHS */
98516914Seric 				m = &mlist[rp[1] - '1'];
98656678Seric 				if (m < mlist || m >= mlp)
9879374Seric 				{
98858151Seric 					syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
98956326Seric 						ruleset, rp[1]);
99059084Seric 					return EX_CONFIG;
9919374Seric 				}
99216914Seric 				if (tTd(21, 15))
99316914Seric 				{
99416914Seric 					printf("$%c:", rp[1]);
99516914Seric 					pp = m->first;
99656678Seric 					while (pp <= m->last)
99716914Seric 					{
99816914Seric 						printf(" %x=\"", *pp);
99916914Seric 						(void) fflush(stdout);
100016914Seric 						printf("%s\"", *pp++);
100116914Seric 					}
100216914Seric 					printf("\n");
100316914Seric 				}
10049374Seric 				pp = m->first;
100556678Seric 				while (pp <= m->last)
10063149Seric 				{
100716914Seric 					if (avp >= &npvp[MAXATOM])
100856678Seric 					{
100958151Seric 						syserr("554 rewrite: expansion too long");
101059084Seric 						return EX_DATAERR;
101156678Seric 					}
101216914Seric 					*avp++ = *pp++;
10133149Seric 				}
10143149Seric 			}
101516914Seric 			else
10168226Seric 			{
101716914Seric 				/* vanilla replacement */
10189374Seric 				if (avp >= &npvp[MAXATOM])
101916889Seric 				{
102056678Seric 	toolong:
102158151Seric 					syserr("554 rewrite: expansion too long");
102259084Seric 					return EX_DATAERR;
102316889Seric 				}
102459027Seric 				if ((*rp & 0377) != MACRODEXPAND)
102559027Seric 					*avp++ = rp;
102659027Seric 				else
102759027Seric 				{
102859027Seric 					*avp = macvalue(rp[1], e);
102959027Seric 					if (tTd(21, 2))
103059027Seric 						printf("rewrite: RHS $&%c => \"%s\"\n",
103159027Seric 							rp[1],
103259027Seric 							*avp == NULL ? "(NULL)" : *avp);
103359027Seric 					if (*avp != NULL)
103459027Seric 						avp++;
103559027Seric 				}
10368226Seric 			}
10379374Seric 		}
10389374Seric 		*avp++ = NULL;
103916914Seric 
104016914Seric 		/*
104156678Seric 		**  Check for any hostname/keyword lookups.
104216914Seric 		*/
104316914Seric 
104416914Seric 		for (rvp = npvp; *rvp != NULL; rvp++)
104516914Seric 		{
104656678Seric 			char **hbrvp;
104716914Seric 			char **xpvp;
104816914Seric 			int trsize;
104956678Seric 			char *replac;
105056678Seric 			int endtoken;
105156678Seric 			STAB *map;
105256678Seric 			char *mapname;
105356678Seric 			char **key_rvp;
105456678Seric 			char **arg_rvp;
105556678Seric 			char **default_rvp;
105656678Seric 			char buf[MAXNAME + 1];
105716914Seric 			char *pvpb1[MAXATOM + 1];
105856823Seric 			char *argvect[10];
105917174Seric 			char pvpbuf[PSBUFSIZE];
106064404Seric 			char *nullpvp[1];
106116914Seric 
106258050Seric 			if ((**rvp & 0377) != HOSTBEGIN &&
106358050Seric 			    (**rvp & 0377) != LOOKUPBEGIN)
106416914Seric 				continue;
106516914Seric 
106616914Seric 			/*
106756678Seric 			**  Got a hostname/keyword lookup.
106816914Seric 			**
106916914Seric 			**	This could be optimized fairly easily.
107016914Seric 			*/
107116914Seric 
107216914Seric 			hbrvp = rvp;
107358050Seric 			if ((**rvp & 0377) == HOSTBEGIN)
107456327Seric 			{
107556678Seric 				endtoken = HOSTEND;
107656678Seric 				mapname = "host";
107756327Seric 			}
107856326Seric 			else
107956327Seric 			{
108056678Seric 				endtoken = LOOKUPEND;
108156678Seric 				mapname = *++rvp;
108256327Seric 			}
108356678Seric 			map = stab(mapname, ST_MAP, ST_FIND);
108456678Seric 			if (map == NULL)
108558151Seric 				syserr("554 rewrite: map %s not found", mapname);
108656678Seric 
108756678Seric 			/* extract the match part */
108856678Seric 			key_rvp = ++rvp;
108956823Seric 			default_rvp = NULL;
109056823Seric 			arg_rvp = argvect;
109156823Seric 			xpvp = NULL;
109256823Seric 			replac = pvpbuf;
109358050Seric 			while (*rvp != NULL && (**rvp & 0377) != endtoken)
109453654Seric 			{
109558050Seric 				int nodetype = **rvp & 0377;
109656823Seric 
109756823Seric 				if (nodetype != CANONHOST && nodetype != CANONUSER)
109856678Seric 				{
109956823Seric 					rvp++;
110056823Seric 					continue;
110156823Seric 				}
110256823Seric 
110356823Seric 				*rvp++ = NULL;
110456823Seric 
110556823Seric 				if (xpvp != NULL)
110656823Seric 				{
110758814Seric 					cataddr(xpvp, NULL, replac,
110858082Seric 						&pvpbuf[sizeof pvpbuf] - replac,
110958082Seric 						'\0');
111056823Seric 					*++arg_rvp = replac;
111156823Seric 					replac += strlen(replac) + 1;
111256823Seric 					xpvp = NULL;
111356823Seric 				}
111456823Seric 				switch (nodetype)
111556823Seric 				{
111656678Seric 				  case CANONHOST:
111756823Seric 					xpvp = rvp;
111856678Seric 					break;
111956678Seric 
112056678Seric 				  case CANONUSER:
112156678Seric 					default_rvp = rvp;
112256678Seric 					break;
112356678Seric 				}
112453654Seric 			}
112516914Seric 			if (*rvp != NULL)
112616914Seric 				*rvp++ = NULL;
112756823Seric 			if (xpvp != NULL)
112856823Seric 			{
112958814Seric 				cataddr(xpvp, NULL, replac,
113058082Seric 					&pvpbuf[sizeof pvpbuf] - replac,
113158082Seric 					'\0');
113256823Seric 				*++arg_rvp = replac;
113356823Seric 			}
113456823Seric 			*++arg_rvp = NULL;
113516914Seric 
113616914Seric 			/* save the remainder of the input string */
113716914Seric 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
113816914Seric 			bcopy((char *) rvp, (char *) pvpb1, trsize);
113916914Seric 
114056678Seric 			/* look it up */
114158814Seric 			cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
114256823Seric 			argvect[0] = buf;
114360538Seric 			if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags))
114456836Seric 			{
114559084Seric 				auto int stat = EX_OK;
114656836Seric 
114769703Seric 				if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
114869703Seric 					stripquotes(buf);
114969703Seric 
115060215Seric 				/* XXX should try to auto-open the map here */
115160215Seric 
115258796Seric 				if (tTd(60, 1))
115358796Seric 					printf("map_lookup(%s, %s) => ",
115458796Seric 						mapname, buf);
115556836Seric 				replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
115660089Seric 						buf, argvect, &stat);
115758796Seric 				if (tTd(60, 1))
115859084Seric 					printf("%s (%d)\n",
115959084Seric 						replac ? replac : "NOT FOUND",
116059084Seric 						stat);
116159084Seric 
116259084Seric 				/* should recover if stat == EX_TEMPFAIL */
116368881Seric 				if (stat == EX_TEMPFAIL)
116468706Seric 				{
116568706Seric 					rstat = EX_TEMPFAIL;
1166*69925Seric 					if (tTd(60, 1))
116768706Seric 						printf("map_lookup(%s, %s) failed (stat = %d)\n",
116868706Seric 							mapname, buf, stat);
116968706Seric 					if (e->e_message == NULL)
117068706Seric 					{
117168706Seric 						char mbuf[300];
117268706Seric 
117368706Seric 						sprintf(mbuf, "map %s: lookup (%s) failed",
117468706Seric 							mapname, buf);
117568706Seric 						e->e_message = newstr(mbuf);
117668706Seric 					}
117768706Seric 				}
117856836Seric 			}
117953654Seric 			else
118056678Seric 				replac = NULL;
118156678Seric 
118256678Seric 			/* if no replacement, use default */
118356823Seric 			if (replac == NULL && default_rvp != NULL)
118456823Seric 			{
118560089Seric 				/* create the default */
118660089Seric 				cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
118756823Seric 				replac = buf;
118856823Seric 			}
118956823Seric 
119056678Seric 			if (replac == NULL)
119151317Seric 			{
119256823Seric 				xpvp = key_rvp;
119353654Seric 			}
119464404Seric 			else if (*replac == '\0')
119564404Seric 			{
119664404Seric 				/* null replacement */
119764404Seric 				nullpvp[0] = NULL;
119864404Seric 				xpvp = nullpvp;
119964404Seric 			}
120056678Seric 			else
120153654Seric 			{
120256678Seric 				/* scan the new replacement */
120365066Seric 				xpvp = prescan(replac, '\0', pvpbuf,
120468711Seric 					       sizeof pvpbuf, NULL, NULL);
120553654Seric 				if (xpvp == NULL)
120651317Seric 				{
120758403Seric 					/* prescan already printed error */
120859084Seric 					return EX_DATAERR;
120956678Seric 				}
121051317Seric 			}
121151317Seric 
121216914Seric 			/* append it to the token list */
121356678Seric 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
121456678Seric 			{
121517174Seric 				*avp++ = newstr(*xpvp);
121616920Seric 				if (avp >= &npvp[MAXATOM])
121716914Seric 					goto toolong;
121817174Seric 			}
121916914Seric 
122016914Seric 			/* restore the old trailing information */
122156678Seric 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
122216920Seric 				if (avp >= &npvp[MAXATOM])
122316914Seric 					goto toolong;
122417174Seric 
122556678Seric 			break;
122616914Seric 		}
122716914Seric 
122816914Seric 		/*
122916914Seric 		**  Check for subroutine calls.
123016914Seric 		*/
123116914Seric 
123268559Seric 		if (*npvp != NULL && (**npvp & 0377) == CALLSUBR)
123368559Seric 		{
123468559Seric 			int stat;
123559084Seric 
123668559Seric 			if (npvp[1] == NULL)
123768559Seric 			{
123868559Seric 				syserr("parseaddr: NULL subroutine call in ruleset %d, rule %d",
123968559Seric 					ruleset, ruleno);
124068559Seric 				*pvp = NULL;
124168559Seric 			}
124268559Seric 			else
124368559Seric 			{
124468559Seric 				int ruleset;
124568559Seric 				STAB *s;
124668385Seric 
124768559Seric 				bcopy((char *) &npvp[2], (char *) pvp,
124868559Seric 					(int) (avp - npvp - 2) * sizeof *avp);
124968559Seric 				if (tTd(21, 3))
125068559Seric 					printf("-----callsubr %s\n", npvp[1]);
125169783Seric 				ruleset = strtorwset(npvp[1], NULL, ST_FIND);
125268559Seric 				stat = rewrite(pvp, ruleset, reclevel, e);
125368559Seric 				if (rstat == EX_OK || stat == EX_TEMPFAIL)
125468559Seric 					rstat = stat;
125568559Seric 				if (*pvp != NULL && (**pvp & 0377) == CANONNET)
125668559Seric 				rwr = NULL;
125768559Seric 			}
125868559Seric 		}
125968559Seric 		else
126068559Seric 		{
126168559Seric 			bcopy((char *) npvp, (char *) pvp,
126268559Seric 				(int) (avp - npvp) * sizeof *avp);
126368559Seric 		}
12649374Seric 		if (tTd(21, 4))
12659374Seric 		{
12669374Seric 			printf("rewritten as:");
126756678Seric 			printav(pvp);
12689374Seric 		}
1269297Seric 	}
12708069Seric 
127167262Seric 	if (OpMode == MD_TEST || tTd(21, 1))
12728069Seric 	{
12738959Seric 		printf("rewrite: ruleset %2d returns:", ruleset);
127456678Seric 		printav(pvp);
12758069Seric 	}
127659084Seric 
127759084Seric 	return rstat;
12783149Seric }
12793149Seric /*
12803149Seric **  BUILDADDR -- build address from token vector.
12813149Seric **
12823149Seric **	Parameters:
12833149Seric **		tv -- token vector.
12843149Seric **		a -- pointer to address descriptor to fill.
12853149Seric **			If NULL, one will be allocated.
128664284Seric **		flags -- info regarding whether this is a sender or
128764284Seric **			a recipient.
128858966Seric **		e -- the current envelope.
12893149Seric **
12903149Seric **	Returns:
12914279Seric **		NULL if there was an error.
12924279Seric **		'a' otherwise.
12933149Seric **
12943149Seric **	Side Effects:
12953149Seric **		fills in 'a'
12963149Seric */
12973149Seric 
129857249Seric struct errcodes
129957249Seric {
130057249Seric 	char	*ec_name;		/* name of error code */
130157249Seric 	int	ec_code;		/* numeric code */
130257249Seric } ErrorCodes[] =
130357249Seric {
130457249Seric 	"usage",	EX_USAGE,
130557249Seric 	"nouser",	EX_NOUSER,
130657249Seric 	"nohost",	EX_NOHOST,
130757249Seric 	"unavailable",	EX_UNAVAILABLE,
130857249Seric 	"software",	EX_SOFTWARE,
130957249Seric 	"tempfail",	EX_TEMPFAIL,
131057249Seric 	"protocol",	EX_PROTOCOL,
131157249Seric #ifdef EX_CONFIG
131257249Seric 	"config",	EX_CONFIG,
131357249Seric #endif
131457249Seric 	NULL,		EX_UNAVAILABLE,
131557249Seric };
131657249Seric 
131756678Seric ADDRESS *
buildaddr(tv,a,flags,e)131864284Seric buildaddr(tv, a, flags, e)
13193149Seric 	register char **tv;
13203149Seric 	register ADDRESS *a;
132164284Seric 	int flags;
132258966Seric 	register ENVELOPE *e;
13233149Seric {
13243149Seric 	struct mailer **mp;
13253149Seric 	register struct mailer *m;
132668849Seric 	register char *p;
132768849Seric 	char *mname;
132868849Seric 	char **hostp;
132968849Seric 	char hbuf[MAXNAME + 1];
133064306Seric 	static MAILER errormailer;
133164306Seric 	static char *errorargv[] = { "ERROR", NULL };
133268849Seric 	static char ubuf[MAXNAME + 1];
13333149Seric 
133464791Seric 	if (tTd(24, 5))
133564791Seric 	{
133667693Seric 		printf("buildaddr, flags=%x, tv=", flags);
133764791Seric 		printav(tv);
133864791Seric 	}
133964791Seric 
13403149Seric 	if (a == NULL)
13413149Seric 		a = (ADDRESS *) xalloc(sizeof *a);
134216889Seric 	bzero((char *) a, sizeof *a);
13433149Seric 
134467880Seric 	/* set up default error return flags */
134567963Seric 	a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
134667880Seric 
13473149Seric 	/* figure out what net/mailer to use */
134864306Seric 	if (*tv == NULL || (**tv & 0377) != CANONNET)
13494279Seric 	{
135058151Seric 		syserr("554 buildaddr: no net");
135164306Seric badaddr:
135264306Seric 		a->q_flags |= QBADADDR;
135364306Seric 		a->q_mailer = &errormailer;
135464306Seric 		if (errormailer.m_name == NULL)
135564306Seric 		{
135664306Seric 			/* initialize the bogus mailer */
135764306Seric 			errormailer.m_name = "*error*";
135864306Seric 			errormailer.m_mailer = "ERROR";
135964306Seric 			errormailer.m_argv = errorargv;
136064306Seric 		}
136164306Seric 		return a;
13624279Seric 	}
136368849Seric 	mname = *++tv;
136468849Seric 
136568849Seric 	/* extract host and user portions */
136668849Seric 	if ((**++tv & 0377) == CANONHOST)
136768849Seric 		hostp = ++tv;
136868849Seric 	else
136968849Seric 		hostp = NULL;
137068849Seric 	while (*tv != NULL && (**tv & 0377) != CANONUSER)
137168849Seric 		tv++;
137268849Seric 	if (*tv == NULL)
13734279Seric 	{
137468849Seric 		syserr("554 buildaddr: no user");
137568849Seric 		goto badaddr;
137668849Seric 	}
137768849Seric 	if (hostp != NULL)
137868849Seric 		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
137968849Seric 	cataddr(++tv, NULL, ubuf, sizeof ubuf, '\0');
138068849Seric 
138168849Seric 	/* save away the host name */
138268849Seric 	if (strcasecmp(mname, "error") == 0)
138368849Seric 	{
138468849Seric 		if (hostp != NULL)
138510183Seric 		{
138657249Seric 			register struct errcodes *ep;
138757249Seric 
138868849Seric 			if (strchr(hbuf, '.') != NULL)
138957249Seric 			{
139068849Seric 				a->q_status = newstr(hbuf);
139168849Seric 				setstat(dsntoexitstat(hbuf));
139257249Seric 			}
139368849Seric 			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
139468849Seric 			{
139568849Seric 				setstat(atoi(hbuf));
139668849Seric 			}
139757249Seric 			else
139857249Seric 			{
139957249Seric 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
140068849Seric 					if (strcasecmp(ep->ec_name, hbuf) == 0)
140157249Seric 						break;
140257249Seric 				setstat(ep->ec_code);
140357249Seric 			}
140410183Seric 		}
140564928Seric 		else
140664928Seric 			setstat(EX_UNAVAILABLE);
140768849Seric 		stripquotes(ubuf);
140868849Seric 		if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
140968849Seric 		    isascii(ubuf[1]) && isdigit(ubuf[1]) &&
141068849Seric 		    isascii(ubuf[2]) && isdigit(ubuf[2]) &&
141168849Seric 		    ubuf[3] == ' ')
141264659Seric 		{
141364659Seric 			char fmt[10];
141464659Seric 
141568849Seric 			strncpy(fmt, ubuf, 3);
141664659Seric 			strcpy(&fmt[3], " %s");
141768849Seric 			usrerr(fmt, ubuf + 4);
141867786Seric 
141967786Seric 			/*
142067786Seric 			**  If this is a 4xx code and we aren't running
142167786Seric 			**  SMTP on our input, bounce this message;
142267786Seric 			**  otherwise it disappears without a trace.
142367786Seric 			*/
142467786Seric 
142567786Seric 			if (fmt[0] == '4' && OpMode != MD_SMTP &&
142667786Seric 			    OpMode != MD_DAEMON)
142767786Seric 			{
142867786Seric 				e->e_flags |= EF_FATALERRS;
142967786Seric 			}
143064659Seric 		}
143164659Seric 		else
143264659Seric 		{
143368849Seric 			usrerr("553 %s", ubuf);
143464659Seric 		}
143564306Seric 		goto badaddr;
14364279Seric 	}
143757402Seric 
14384598Seric 	for (mp = Mailer; (m = *mp++) != NULL; )
14393149Seric 	{
144068849Seric 		if (strcasecmp(m->m_name, mname) == 0)
14413149Seric 			break;
14423149Seric 	}
14433149Seric 	if (m == NULL)
14444279Seric 	{
144568849Seric 		syserr("554 buildaddr: unknown mailer %s", mname);
144664306Seric 		goto badaddr;
14474279Seric 	}
14484598Seric 	a->q_mailer = m;
14493149Seric 
14503149Seric 	/* figure out what host (if any) */
145168849Seric 	if (hostp == NULL)
14523149Seric 	{
145358509Seric 		if (!bitnset(M_LOCALMAILER, m->m_flags))
145458509Seric 		{
145558509Seric 			syserr("554 buildaddr: no host");
145664306Seric 			goto badaddr;
145758509Seric 		}
145857249Seric 		a->q_host = NULL;
145958509Seric 	}
146068849Seric 	else
146168849Seric 		a->q_host = newstr(hbuf);
14623149Seric 
14633149Seric 	/* figure out the user */
146468849Seric 	p = ubuf;
146568849Seric 	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
14664279Seric 	{
146768849Seric 		p++;
146868098Seric 		tv++;
146968098Seric 		a->q_flags |= QNOTREMOTE;
147068098Seric 	}
147168098Seric 
147257402Seric 	/* do special mapping for local mailer */
147368849Seric 	if (*p == '"')
147468849Seric 		p++;
147568849Seric 	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
147668849Seric 		a->q_mailer = m = ProgMailer;
147768849Seric 	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
147868849Seric 		a->q_mailer = m = FileMailer;
147968849Seric 	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
148057402Seric 	{
148168849Seric 		/* may be :include: */
148268849Seric 		stripquotes(ubuf);
148368849Seric 		if (strncasecmp(ubuf, ":include:", 9) == 0)
148457402Seric 		{
148568849Seric 			/* if :include:, don't need further rewriting */
148668849Seric 			a->q_mailer = m = InclMailer;
148768849Seric 			a->q_user = newstr(&ubuf[9]);
148868849Seric 			return a;
148957402Seric 		}
149057402Seric 	}
149157402Seric 
149264284Seric 	/* rewrite according recipient mailer rewriting rules */
149364284Seric 	define('h', a->q_host, e);
149464284Seric 	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
149564284Seric 	{
149664284Seric 		/* sender addresses done later */
149765071Seric 		(void) rewrite(tv, 2, 0, e);
149864284Seric 		if (m->m_re_rwset > 0)
149965071Seric 		       (void) rewrite(tv, m->m_re_rwset, 0, e);
150064284Seric 	}
150165071Seric 	(void) rewrite(tv, 4, 0, e);
150219040Seric 
150319040Seric 	/* save the result for the command line/RCPT argument */
150468849Seric 	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
150568849Seric 	a->q_user = ubuf;
15063149Seric 
150758670Seric 	/*
150858670Seric 	**  Do mapping to lower case as requested by mailer
150958670Seric 	*/
151058670Seric 
151158670Seric 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
151258670Seric 		makelower(a->q_host);
151358670Seric 	if (!bitnset(M_USR_UPPER, m->m_flags))
151458670Seric 		makelower(a->q_user);
151558670Seric 
151668849Seric 	return a;
15173149Seric }
15183188Seric /*
15194228Seric **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
15204228Seric **
15214228Seric **	Parameters:
15224228Seric **		pvp -- parameter vector to rebuild.
152358814Seric **		evp -- last parameter to include.  Can be NULL to
152458814Seric **			use entire pvp.
15254228Seric **		buf -- buffer to build the string into.
15264228Seric **		sz -- size of buf.
152758082Seric **		spacesub -- the space separator character; if null,
152858082Seric **			use SpaceSub.
15294228Seric **
15304228Seric **	Returns:
15314228Seric **		none.
15324228Seric **
15334228Seric **	Side Effects:
15344228Seric **		Destroys buf.
15354228Seric */
15364228Seric 
153769748Seric void
cataddr(pvp,evp,buf,sz,spacesub)153858814Seric cataddr(pvp, evp, buf, sz, spacesub)
15394228Seric 	char **pvp;
154058814Seric 	char **evp;
15414228Seric 	char *buf;
15424228Seric 	register int sz;
154369748Seric 	int spacesub;
15444228Seric {
15454228Seric 	bool oatomtok = FALSE;
154656678Seric 	bool natomtok = FALSE;
15474228Seric 	register int i;
15484228Seric 	register char *p;
15494228Seric 
155058082Seric 	if (spacesub == '\0')
155158082Seric 		spacesub = SpaceSub;
155258082Seric 
15538423Seric 	if (pvp == NULL)
15548423Seric 	{
155523109Seric 		(void) strcpy(buf, "");
15568423Seric 		return;
15578423Seric 	}
15584228Seric 	p = buf;
155911156Seric 	sz -= 2;
15604228Seric 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
15614228Seric 	{
156268711Seric 		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
15634228Seric 		if (oatomtok && natomtok)
156458082Seric 			*p++ = spacesub;
15654228Seric 		(void) strcpy(p, *pvp);
15664228Seric 		oatomtok = natomtok;
15674228Seric 		p += i;
156811156Seric 		sz -= i + 1;
156958814Seric 		if (pvp++ == evp)
157058814Seric 			break;
15714228Seric 	}
15724228Seric 	*p = '\0';
15734228Seric }
15744228Seric /*
15753188Seric **  SAMEADDR -- Determine if two addresses are the same
15763188Seric **
15773188Seric **	This is not just a straight comparison -- if the mailer doesn't
15783188Seric **	care about the host we just ignore it, etc.
15793188Seric **
15803188Seric **	Parameters:
15813188Seric **		a, b -- pointers to the internal forms to compare.
15823188Seric **
15833188Seric **	Returns:
15843188Seric **		TRUE -- they represent the same mailbox.
15853188Seric **		FALSE -- they don't.
15863188Seric **
15873188Seric **	Side Effects:
15883188Seric **		none.
15893188Seric */
15903188Seric 
15913188Seric bool
sameaddr(a,b)15929374Seric sameaddr(a, b)
15933188Seric 	register ADDRESS *a;
15943188Seric 	register ADDRESS *b;
15953188Seric {
159665093Seric 	register ADDRESS *ca, *cb;
159765093Seric 
15983188Seric 	/* if they don't have the same mailer, forget it */
15993188Seric 	if (a->q_mailer != b->q_mailer)
16003188Seric 		return (FALSE);
16013188Seric 
16023188Seric 	/* if the user isn't the same, we can drop out */
160356678Seric 	if (strcmp(a->q_user, b->q_user) != 0)
16043188Seric 		return (FALSE);
16053188Seric 
160665093Seric 	/* if we have good uids for both but they differ, these are different */
160765379Seric 	if (a->q_mailer == ProgMailer)
160865379Seric 	{
160965379Seric 		ca = getctladdr(a);
161065379Seric 		cb = getctladdr(b);
161165379Seric 		if (ca != NULL && cb != NULL &&
161265379Seric 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
161365379Seric 		    ca->q_uid != cb->q_uid)
161465379Seric 			return (FALSE);
161565379Seric 	}
161658438Seric 
161758509Seric 	/* otherwise compare hosts (but be careful for NULL ptrs) */
161858509Seric 	if (a->q_host == b->q_host)
161958509Seric 	{
162058509Seric 		/* probably both null pointers */
16213188Seric 		return (TRUE);
162258509Seric 	}
16233188Seric 	if (a->q_host == NULL || b->q_host == NULL)
162458509Seric 	{
162558509Seric 		/* only one is a null pointer */
16263188Seric 		return (FALSE);
162758509Seric 	}
162856678Seric 	if (strcmp(a->q_host, b->q_host) != 0)
16293188Seric 		return (FALSE);
16303188Seric 
16313188Seric 	return (TRUE);
16323188Seric }
16333234Seric /*
16343234Seric **  PRINTADDR -- print address (for debugging)
16353234Seric **
16363234Seric **	Parameters:
16373234Seric **		a -- the address to print
16383234Seric **		follow -- follow the q_next chain.
16393234Seric **
16403234Seric **	Returns:
16413234Seric **		none.
16423234Seric **
16433234Seric **	Side Effects:
16443234Seric **		none.
16453234Seric */
16463234Seric 
164767994Seric struct qflags
164867994Seric {
164967994Seric 	char	*qf_name;
165067994Seric 	u_long	qf_bit;
165167994Seric };
165267994Seric 
165367994Seric struct qflags	AddressFlags[] =
165467994Seric {
165567994Seric 	"QDONTSEND",		QDONTSEND,
165667994Seric 	"QBADADDR",		QBADADDR,
165767994Seric 	"QGOODUID",		QGOODUID,
165867994Seric 	"QPRIMARY",		QPRIMARY,
165967994Seric 	"QQUEUEUP",		QQUEUEUP,
166067994Seric 	"QSENT",		QSENT,
166167994Seric 	"QNOTREMOTE",		QNOTREMOTE,
166267994Seric 	"QSELFREF",		QSELFREF,
166367994Seric 	"QVERIFIED",		QVERIFIED,
166467994Seric 	"QBOGUSSHELL",		QBOGUSSHELL,
166567994Seric 	"QUNSAFEADDR",		QUNSAFEADDR,
166667994Seric 	"QPINGONSUCCESS",	QPINGONSUCCESS,
166767994Seric 	"QPINGONFAILURE",	QPINGONFAILURE,
166867994Seric 	"QPINGONDELAY",		QPINGONDELAY,
166968595Seric 	"QHASNOTIFY",		QHASNOTIFY,
167067994Seric 	"QRELAYED",		QRELAYED,
167168867Seric 	"QEXPANDED",		QEXPANDED,
167268867Seric 	"QDELIVERED",		QDELIVERED,
167368867Seric 	"QDELAYED",		QDELAYED,
167468603Seric 	"QTHISPASS",		QTHISPASS,
167567994Seric 	NULL
167667994Seric };
167767994Seric 
167868433Seric void
printaddr(a,follow)16793234Seric printaddr(a, follow)
16803234Seric 	register ADDRESS *a;
16813234Seric 	bool follow;
16823234Seric {
168357731Seric 	register MAILER *m;
168457731Seric 	MAILER pseudomailer;
168567994Seric 	register struct qflags *qfp;
168667994Seric 	bool firstone;
16875001Seric 
168867994Seric 	if (a == NULL)
168967994Seric 	{
169067994Seric 		printf("[NULL]\n");
169167994Seric 		return;
169267994Seric 	}
169367994Seric 
16943234Seric 	while (a != NULL)
16953234Seric 	{
16964443Seric 		printf("%x=", a);
16974085Seric 		(void) fflush(stdout);
169857731Seric 
169957731Seric 		/* find the mailer -- carefully */
170057731Seric 		m = a->q_mailer;
170157731Seric 		if (m == NULL)
170257731Seric 		{
170357731Seric 			m = &pseudomailer;
170457731Seric 			m->m_mno = -1;
170557731Seric 			m->m_name = "NULL";
170657731Seric 		}
170757731Seric 
170868603Seric 		printf("%s:\n\tmailer %d (%s), host `%s'\n",
170957731Seric 		       a->q_paddr, m->m_mno, m->m_name,
171068603Seric 		       a->q_host == NULL ? "<null>" : a->q_host);
171168603Seric 		printf("\tuser `%s', ruser `%s'\n",
171268603Seric 		       a->q_user,
171367172Seric 		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
171467994Seric 		printf("\tnext=%x, alias %x, uid %d, gid %d\n",
171567994Seric 		       a->q_next, a->q_alias, a->q_uid, a->q_gid);
171667994Seric 		printf("\tflags=%lx<", a->q_flags);
171767994Seric 		firstone = TRUE;
171867994Seric 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
171967994Seric 		{
172067994Seric 			if (!bitset(qfp->qf_bit, a->q_flags))
172167994Seric 				continue;
172267994Seric 			if (!firstone)
172367994Seric 				printf(",");
172467994Seric 			firstone = FALSE;
172567994Seric 			printf("%s", qfp->qf_name);
172667994Seric 		}
172767994Seric 		printf(">\n");
172859269Seric 		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
172959269Seric 		       a->q_owner == NULL ? "(none)" : a->q_owner,
173063756Seric 		       a->q_home == NULL ? "(none)" : a->q_home,
173163756Seric 		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
173268228Seric 		printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n",
173367987Seric 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
173467987Seric 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
173567990Seric 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
17364996Seric 
17373234Seric 		if (!follow)
17383234Seric 			return;
17394996Seric 		a = a->q_next;
17403234Seric 	}
17413234Seric }
174267939Seric /*
174367939Seric **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
174467939Seric **
174567939Seric **	Parameters:
174667939Seric **		a -- pointer to the address
174767939Seric **
174867939Seric **	Returns:
174967939Seric **		TRUE -- if this address is "empty" (i.e., no one should
175067939Seric **			ever generate replies to it.
175167939Seric **		FALSE -- if it is a "regular" (read: replyable) address.
175267939Seric */
17534317Seric 
175467939Seric bool
emptyaddr(a)175567939Seric emptyaddr(a)
175667939Seric 	register ADDRESS *a;
175767939Seric {
175867939Seric 	return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
175967939Seric }
17607682Seric /*
17617682Seric **  REMOTENAME -- return the name relative to the current mailer
17627682Seric **
17637682Seric **	Parameters:
17647682Seric **		name -- the name to translate.
17658069Seric **		m -- the mailer that we want to do rewriting relative
17668069Seric **			to.
176759163Seric **		flags -- fine tune operations.
176859163Seric **		pstat -- pointer to status word.
176958020Seric **		e -- the current envelope.
17707682Seric **
17717682Seric **	Returns:
17727682Seric **		the text string representing this address relative to
17737682Seric **			the receiving mailer.
17747682Seric **
17757682Seric **	Side Effects:
17767682Seric **		none.
17777682Seric **
17787682Seric **	Warnings:
17797682Seric **		The text string returned is tucked away locally;
17807682Seric **			copy it if you intend to save it.
17817682Seric */
17827682Seric 
17837682Seric char *
remotename(name,m,flags,pstat,e)178459163Seric remotename(name, m, flags, pstat, e)
17857682Seric 	char *name;
178656678Seric 	struct mailer *m;
178759163Seric 	int flags;
178859163Seric 	int *pstat;
178956678Seric 	register ENVELOPE *e;
17907682Seric {
17918069Seric 	register char **pvp;
17928069Seric 	char *fancy;
179356678Seric 	char *oldg = macvalue('g', e);
179458020Seric 	int rwset;
179568528Seric 	static char buf[MAXNAME + 1];
179668528Seric 	char lbuf[MAXNAME + 1];
179716914Seric 	char pvpbuf[PSBUFSIZE];
179856678Seric 	extern char *crackaddr();
17997682Seric 
18007755Seric 	if (tTd(12, 1))
18017755Seric 		printf("remotename(%s)\n", name);
18027755Seric 
180310177Seric 	/* don't do anything if we are tagging it as special */
180459163Seric 	if (bitset(RF_SENDERADDR, flags))
180559163Seric 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
180659163Seric 						     : m->m_se_rwset;
180758020Seric 	else
180859163Seric 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
180959163Seric 						     : m->m_re_rwset;
181058020Seric 	if (rwset < 0)
181110177Seric 		return (name);
181210177Seric 
18137682Seric 	/*
18148181Seric 	**  Do a heuristic crack of this name to extract any comment info.
18158181Seric 	**	This will leave the name as a comment and a $g macro.
18167889Seric 	*/
18177889Seric 
181859163Seric 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
181958050Seric 		fancy = "\201g";
182010310Seric 	else
182110310Seric 		fancy = crackaddr(name);
18227889Seric 
18238181Seric 	/*
18248181Seric 	**  Turn the name into canonical form.
18258181Seric 	**	Normally this will be RFC 822 style, i.e., "user@domain".
18268181Seric 	**	If this only resolves to "user", and the "C" flag is
18278181Seric 	**	specified in the sending mailer, then the sender's
18288181Seric 	**	domain will be appended.
18298181Seric 	*/
18308181Seric 
183168711Seric 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
18327889Seric 	if (pvp == NULL)
18337889Seric 		return (name);
183465071Seric 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
183559163Seric 		*pstat = EX_TEMPFAIL;
183659163Seric 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
18378181Seric 	{
18388181Seric 		/* append from domain to this address */
18398181Seric 		register char **pxp = pvp;
18408181Seric 
18419594Seric 		/* see if there is an "@domain" in the current name */
18428181Seric 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
18438181Seric 			pxp++;
18448181Seric 		if (*pxp == NULL)
18458181Seric 		{
18469594Seric 			/* no.... append the "@domain" from the sender */
184756678Seric 			register char **qxq = e->e_fromdomain;
18488181Seric 
18499594Seric 			while ((*pxp++ = *qxq++) != NULL)
18509594Seric 				continue;
185165071Seric 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
185259163Seric 				*pstat = EX_TEMPFAIL;
18538181Seric 		}
18548181Seric 	}
18558181Seric 
18568181Seric 	/*
18578959Seric 	**  Do more specific rewriting.
185856678Seric 	**	Rewrite using ruleset 1 or 2 depending on whether this is
185956678Seric 	**		a sender address or not.
18608181Seric 	**	Then run it through any receiving-mailer-specific rulesets.
18618181Seric 	*/
18628181Seric 
186359163Seric 	if (bitset(RF_SENDERADDR, flags))
186459541Seric 	{
186565071Seric 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
186659163Seric 			*pstat = EX_TEMPFAIL;
186759541Seric 	}
18688069Seric 	else
186959541Seric 	{
187065071Seric 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
187159163Seric 			*pstat = EX_TEMPFAIL;
187259541Seric 	}
187358020Seric 	if (rwset > 0)
187459541Seric 	{
187565071Seric 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
187659163Seric 			*pstat = EX_TEMPFAIL;
187759541Seric 	}
18787682Seric 
18798181Seric 	/*
18808959Seric 	**  Do any final sanitation the address may require.
18818959Seric 	**	This will normally be used to turn internal forms
18828959Seric 	**	(e.g., user@host.LOCAL) into external form.  This
18838959Seric 	**	may be used as a default to the above rules.
18848959Seric 	*/
18858959Seric 
188665071Seric 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
188759163Seric 		*pstat = EX_TEMPFAIL;
18888959Seric 
18898959Seric 	/*
18908181Seric 	**  Now restore the comment information we had at the beginning.
18918181Seric 	*/
18928181Seric 
189358825Seric 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
189456678Seric 	define('g', lbuf, e);
189565494Seric 
189665494Seric 	/* need to make sure route-addrs have <angle brackets> */
189765494Seric 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
189868529Seric 		expand("<\201g>", buf, sizeof buf, e);
189965494Seric 	else
190068529Seric 		expand(fancy, buf, sizeof buf, e);
190165494Seric 
190256678Seric 	define('g', oldg, e);
19037682Seric 
19047682Seric 	if (tTd(12, 1))
19057755Seric 		printf("remotename => `%s'\n", buf);
19067682Seric 	return (buf);
19077682Seric }
190851317Seric /*
190956678Seric **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
191051317Seric **
191151317Seric **	Parameters:
191256678Seric **		a -- the address to map (but just the user name part).
191356678Seric **		sendq -- the sendq in which to install any replacement
191456678Seric **			addresses.
191567982Seric **		aliaslevel -- the alias nesting depth.
191667982Seric **		e -- the envelope.
191751317Seric **
191851317Seric **	Returns:
191951317Seric **		none.
192051317Seric */
192151317Seric 
192269748Seric void
maplocaluser(a,sendq,aliaslevel,e)192367982Seric maplocaluser(a, sendq, aliaslevel, e)
192456678Seric 	register ADDRESS *a;
192556678Seric 	ADDRESS **sendq;
192667982Seric 	int aliaslevel;
192756678Seric 	ENVELOPE *e;
192851317Seric {
192956678Seric 	register char **pvp;
193056678Seric 	register ADDRESS *a1 = NULL;
193158333Seric 	auto char *delimptr;
193256678Seric 	char pvpbuf[PSBUFSIZE];
193351317Seric 
193456678Seric 	if (tTd(29, 1))
193556678Seric 	{
193656678Seric 		printf("maplocaluser: ");
193756678Seric 		printaddr(a, FALSE);
193856678Seric 	}
193968711Seric 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
194056678Seric 	if (pvp == NULL)
194156678Seric 		return;
194251317Seric 
194365071Seric 	(void) rewrite(pvp, 5, 0, e);
194458050Seric 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
194556678Seric 		return;
194651317Seric 
194756678Seric 	/* if non-null, mailer destination specified -- has it changed? */
194864284Seric 	a1 = buildaddr(pvp, NULL, 0, e);
194956678Seric 	if (a1 == NULL || sameaddr(a, a1))
195056678Seric 		return;
195151317Seric 
195256678Seric 	/* mark old address as dead; insert new address */
195356678Seric 	a->q_flags |= QDONTSEND;
195457731Seric 	if (tTd(29, 5))
195557731Seric 	{
195657731Seric 		printf("maplocaluser: QDONTSEND ");
195757731Seric 		printaddr(a, FALSE);
195857731Seric 	}
195956678Seric 	a1->q_alias = a;
196064348Seric 	allocaddr(a1, RF_COPYALL, NULL);
196167982Seric 	(void) recipient(a1, sendq, aliaslevel, e);
196251317Seric }
196358802Seric /*
196458802Seric **  DEQUOTE_INIT -- initialize dequote map
196558802Seric **
196658802Seric **	This is a no-op.
196758802Seric **
196858802Seric **	Parameters:
196958802Seric **		map -- the internal map structure.
197058802Seric **		args -- arguments.
197158802Seric **
197258802Seric **	Returns:
197358802Seric **		TRUE.
197458802Seric */
197558802Seric 
197658802Seric bool
dequote_init(map,args)197760219Seric dequote_init(map, args)
197858802Seric 	MAP *map;
197958802Seric 	char *args;
198058802Seric {
198158805Seric 	register char *p = args;
198258805Seric 
198369816Seric 	map->map_mflags |= MF_KEEPQUOTES;
198458805Seric 	for (;;)
198558805Seric 	{
198658805Seric 		while (isascii(*p) && isspace(*p))
198758805Seric 			p++;
198858805Seric 		if (*p != '-')
198958805Seric 			break;
199058805Seric 		switch (*++p)
199158805Seric 		{
199258805Seric 		  case 'a':
199358805Seric 			map->map_app = ++p;
199458805Seric 			break;
199567824Seric 
199667824Seric 		  case 's':
199767824Seric 			map->map_coldelim = *++p;
199867824Seric 			break;
199958805Seric 		}
200058805Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
200158805Seric 			p++;
200258805Seric 		if (*p != '\0')
200358805Seric 			*p = '\0';
200458805Seric 	}
200558805Seric 	if (map->map_app != NULL)
200658805Seric 		map->map_app = newstr(map->map_app);
200758805Seric 
200858802Seric 	return TRUE;
200958802Seric }
201058802Seric /*
201158802Seric **  DEQUOTE_MAP -- unquote an address
201258802Seric **
201358802Seric **	Parameters:
201458802Seric **		map -- the internal map structure (ignored).
201560089Seric **		name -- the name to dequote.
201658802Seric **		av -- arguments (ignored).
201759084Seric **		statp -- pointer to status out-parameter.
201858802Seric **
201958802Seric **	Returns:
202058802Seric **		NULL -- if there were no quotes, or if the resulting
202158802Seric **			unquoted buffer would not be acceptable to prescan.
202258802Seric **		else -- The dequoted buffer.
202358802Seric */
202458802Seric 
202558802Seric char *
dequote_map(map,name,av,statp)202660089Seric dequote_map(map, name, av, statp)
202758802Seric 	MAP *map;
202860089Seric 	char *name;
202958802Seric 	char **av;
203059084Seric 	int *statp;
203158802Seric {
203258802Seric 	register char *p;
203358802Seric 	register char *q;
203458802Seric 	register char c;
203567824Seric 	int anglecnt = 0;
203667824Seric 	int cmntcnt = 0;
203767824Seric 	int quotecnt = 0;
203867824Seric 	int spacecnt = 0;
203967824Seric 	bool quotemode = FALSE;
204067824Seric 	bool bslashmode = FALSE;
204167824Seric 	char spacesub = map->map_coldelim;
204258802Seric 
204360089Seric 	for (p = q = name; (c = *p++) != '\0'; )
204458802Seric 	{
204558802Seric 		if (bslashmode)
204658802Seric 		{
204758802Seric 			bslashmode = FALSE;
204858802Seric 			*q++ = c;
204958802Seric 			continue;
205058802Seric 		}
205158802Seric 
205267824Seric 		if (c == ' ' && spacesub != '\0')
205367824Seric 			c = spacesub;
205467764Seric 
205558802Seric 		switch (c)
205658802Seric 		{
205758802Seric 		  case '\\':
205858802Seric 			bslashmode = TRUE;
205958802Seric 			break;
206058802Seric 
206158802Seric 		  case '(':
206258802Seric 			cmntcnt++;
206358802Seric 			break;
206458802Seric 
206558802Seric 		  case ')':
206658802Seric 			if (cmntcnt-- <= 0)
206758802Seric 				return NULL;
206858802Seric 			break;
206959089Seric 
207059089Seric 		  case ' ':
207159089Seric 			spacecnt++;
207259089Seric 			break;
207358802Seric 		}
207458802Seric 
207558802Seric 		if (cmntcnt > 0)
207658802Seric 		{
207758802Seric 			*q++ = c;
207858802Seric 			continue;
207958802Seric 		}
208058802Seric 
208158802Seric 		switch (c)
208258802Seric 		{
208358802Seric 		  case '"':
208458802Seric 			quotemode = !quotemode;
208558802Seric 			quotecnt++;
208658802Seric 			continue;
208758802Seric 
208858802Seric 		  case '<':
208958802Seric 			anglecnt++;
209058802Seric 			break;
209158802Seric 
209258802Seric 		  case '>':
209358802Seric 			if (anglecnt-- <= 0)
209458802Seric 				return NULL;
209558802Seric 			break;
209658802Seric 		}
209758802Seric 		*q++ = c;
209858802Seric 	}
209958802Seric 
210058802Seric 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
210159089Seric 	    quotemode || quotecnt <= 0 || spacecnt != 0)
210258802Seric 		return NULL;
210358802Seric 	*q++ = '\0';
210460089Seric 	return name;
210558802Seric }
2106