122976Smiriam /*
234921Sbostic  * Copyright (c) 1983 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*68711Seric static char sccsid[] = "@(#)parseaddr.c	8.64 (Berkeley) 04/02/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 *
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();
68297Seric 
69297Seric 	/*
70297Seric 	**  Initialize and prescan address.
71297Seric 	*/
72297Seric 
7356678Seric 	e->e_to = addr;
747675Seric 	if (tTd(20, 1))
759888Seric 		printf("\n--parseaddr(%s)\n", addr);
763188Seric 
7758333Seric 	if (delimptr == NULL)
7858333Seric 		delimptr = &delimptrbuf;
7958333Seric 
80*68711Seric 	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
813149Seric 	if (pvp == NULL)
8256729Seric 	{
8356729Seric 		if (tTd(20, 1))
8456729Seric 			printf("parseaddr-->NULL\n");
85297Seric 		return (NULL);
8656729Seric 	}
87297Seric 
8864726Seric 	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
8964726Seric 	{
9064726Seric 		if (tTd(20, 1))
9164726Seric 			printf("parseaddr-->bad address\n");
9264726Seric 		return NULL;
9364726Seric 	}
9464726Seric 
95297Seric 	/*
9664348Seric 	**  Save addr if we are going to have to.
9764348Seric 	**
9864348Seric 	**	We have to do this early because there is a chance that
9964348Seric 	**	the map lookups in the rewriting rules could clobber
10064348Seric 	**	static memory somewhere.
10164348Seric 	*/
10264348Seric 
10364348Seric 	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
10464348Seric 	{
10564348Seric 		char savec = **delimptr;
10664348Seric 
10764348Seric 		if (savec != '\0')
10864348Seric 			**delimptr = '\0';
10966794Seric 		e->e_to = addr = newstr(addr);
11064348Seric 		if (savec != '\0')
11164348Seric 			**delimptr = savec;
11264348Seric 	}
11364348Seric 
11464348Seric 	/*
1153149Seric 	**  Apply rewriting rules.
1167889Seric 	**	Ruleset 0 does basic parsing.  It must resolve.
117297Seric 	*/
118297Seric 
11959084Seric 	queueup = FALSE;
12065071Seric 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
12159084Seric 		queueup = TRUE;
12265071Seric 	if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
12359084Seric 		queueup = TRUE;
124297Seric 
125297Seric 
126297Seric 	/*
1273149Seric 	**  Build canonical address from pvp.
128297Seric 	*/
129297Seric 
13064284Seric 	a = buildaddr(pvp, a, flags, e);
131297Seric 
132297Seric 	/*
1333149Seric 	**  Make local copies of the host & user and then
1343149Seric 	**  transport them out.
135297Seric 	*/
136297Seric 
13764348Seric 	allocaddr(a, flags, addr);
13864306Seric 	if (bitset(QBADADDR, a->q_flags))
13964306Seric 		return a;
14056678Seric 
14156678Seric 	/*
14259084Seric 	**  If there was a parsing failure, mark it for queueing.
14359084Seric 	*/
14459084Seric 
14559084Seric 	if (queueup)
14659595Seric 	{
14759734Seric 		char *msg = "Transient parse error -- message queued for future delivery";
14859734Seric 
14959595Seric 		if (tTd(20, 1))
15059595Seric 			printf("parseaddr: queuing message\n");
15159734Seric 		message(msg);
15259734Seric 		if (e->e_message == NULL)
15360009Seric 			e->e_message = newstr(msg);
15459084Seric 		a->q_flags |= QQUEUEUP;
15568358Seric 		a->q_status = "4.4.3";
15659595Seric 	}
15759084Seric 
15859084Seric 	/*
15956678Seric 	**  Compute return value.
16056678Seric 	*/
16156678Seric 
16256678Seric 	if (tTd(20, 1))
1638078Seric 	{
16456678Seric 		printf("parseaddr-->");
16556678Seric 		printaddr(a, FALSE);
16656678Seric 	}
16756678Seric 
16856678Seric 	return (a);
16956678Seric }
17056678Seric /*
17157388Seric **  INVALIDADDR -- check for address containing meta-characters
17257388Seric **
17357388Seric **	Parameters:
17457388Seric **		addr -- the address to check.
17557388Seric **
17657388Seric **	Returns:
17757388Seric **		TRUE -- if the address has any "wierd" characters
17857388Seric **		FALSE -- otherwise.
17957388Seric */
18057388Seric 
18157388Seric bool
18264726Seric invalidaddr(addr, delimptr)
18357388Seric 	register char *addr;
18464726Seric 	char *delimptr;
18557388Seric {
18668433Seric 	char savedelim = '\0';
18764726Seric 
18864726Seric 	if (delimptr != NULL)
18964764Seric 	{
19064726Seric 		savedelim = *delimptr;
19164764Seric 		if (savedelim != '\0')
19264764Seric 			*delimptr = '\0';
19364764Seric 	}
19464726Seric #if 0
19564726Seric 	/* for testing.... */
19664726Seric 	if (strcmp(addr, "INvalidADDR") == 0)
19757388Seric 	{
19864726Seric 		usrerr("553 INvalid ADDRess");
19964764Seric 		goto addrfailure;
20057388Seric 	}
20164726Seric #endif
20264726Seric 	for (; *addr != '\0'; addr++)
20364726Seric 	{
20464726Seric 		if ((*addr & 0340) == 0200)
20564726Seric 			break;
20664726Seric 	}
20764726Seric 	if (*addr == '\0')
20864764Seric 	{
20968400Seric 		if (delimptr != NULL && savedelim != '\0')
21064764Seric 			*delimptr = savedelim;
21164726Seric 		return FALSE;
21264764Seric 	}
21364726Seric 	setstat(EX_USAGE);
21464726Seric 	usrerr("553 Address contained invalid control characters");
21564764Seric   addrfailure:
21668446Seric 	if (delimptr != NULL && savedelim != '\0')
21764764Seric 		*delimptr = savedelim;
21864726Seric 	return TRUE;
21957388Seric }
22057388Seric /*
22156678Seric **  ALLOCADDR -- do local allocations of address on demand.
22256678Seric **
22356678Seric **	Also lowercases the host name if requested.
22456678Seric **
22556678Seric **	Parameters:
22656678Seric **		a -- the address to reallocate.
22764284Seric **		flags -- the copy flag (see RF_ definitions in sendmail.h
22864284Seric **			for a description).
22956678Seric **		paddr -- the printname of the address.
23056678Seric **
23156678Seric **	Returns:
23256678Seric **		none.
23356678Seric **
23456678Seric **	Side Effects:
23556678Seric **		Copies portions of a into local buffers as requested.
23656678Seric */
23756678Seric 
23864348Seric allocaddr(a, flags, paddr)
23956678Seric 	register ADDRESS *a;
24064284Seric 	int flags;
24156678Seric 	char *paddr;
24256678Seric {
24358673Seric 	if (tTd(24, 4))
24467693Seric 		printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
24558673Seric 
24664348Seric 	a->q_paddr = paddr;
2478078Seric 
24824944Seric 	if (a->q_user == NULL)
24924944Seric 		a->q_user = "";
25024944Seric 	if (a->q_host == NULL)
25124944Seric 		a->q_host = "";
25224944Seric 
25364284Seric 	if (bitset(RF_COPYPARSE, flags))
254297Seric 	{
25524944Seric 		a->q_host = newstr(a->q_host);
2563149Seric 		if (a->q_user != a->q_paddr)
2573149Seric 			a->q_user = newstr(a->q_user);
258297Seric 	}
259297Seric 
26056678Seric 	if (a->q_paddr == NULL)
26156678Seric 		a->q_paddr = a->q_user;
262297Seric }
263297Seric /*
264297Seric **  PRESCAN -- Prescan name and make it canonical
265297Seric **
2669374Seric **	Scans a name and turns it into a set of tokens.  This process
2679374Seric **	deletes blanks and comments (in parentheses).
268297Seric **
269297Seric **	This routine knows about quoted strings and angle brackets.
270297Seric **
271297Seric **	There are certain subtleties to this routine.  The one that
272297Seric **	comes to mind now is that backslashes on the ends of names
273297Seric **	are silently stripped off; this is intentional.  The problem
274297Seric **	is that some versions of sndmsg (like at LBL) set the kill
275297Seric **	character to something other than @ when reading addresses;
276297Seric **	so people type "csvax.eric\@berkeley" -- which screws up the
277297Seric **	berknet mailer.
278297Seric **
279297Seric **	Parameters:
280297Seric **		addr -- the name to chomp.
281297Seric **		delim -- the delimiter for the address, normally
282297Seric **			'\0' or ','; \0 is accepted in any case.
28315284Seric **			If '\t' then we are reading the .cf file.
28416914Seric **		pvpbuf -- place to put the saved text -- note that
28516914Seric **			the pointers are static.
28665066Seric **		pvpbsize -- size of pvpbuf.
28758333Seric **		delimptr -- if non-NULL, set to the location of the
28858333Seric **			terminating delimiter.
289*68711Seric **		toktab -- if set, a token table to use for parsing.
290*68711Seric **			If NULL, use the default table.
291297Seric **
292297Seric **	Returns:
2933149Seric **		A pointer to a vector of tokens.
294297Seric **		NULL on error.
295297Seric */
296297Seric 
2978078Seric /* states and character types */
2988078Seric # define OPR		0	/* operator */
2998078Seric # define ATM		1	/* atom */
3008078Seric # define QST		2	/* in quoted string */
3018078Seric # define SPC		3	/* chewing up spaces */
3028078Seric # define ONE		4	/* pick up one character */
303*68711Seric # define ILL		5	/* illegal character */
3043149Seric 
305*68711Seric # define NSTATES	6	/* number of states */
3068078Seric # define TYPE		017	/* mask to select state type */
3078078Seric 
3088078Seric /* meta bits for table */
3098078Seric # define M		020	/* meta character; don't pass through */
3108078Seric # define B		040	/* cause a break */
3118078Seric # define MB		M|B	/* meta-break */
3128078Seric 
3138078Seric static short StateTab[NSTATES][NSTATES] =
3148078Seric {
315*68711Seric    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
316*68711Seric 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB,
317*68711Seric 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB,
318*68711Seric 	/*QST*/		QST,	QST,	OPR,	QST,	QST,	QST,
319*68711Seric 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB,
320*68711Seric 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB,
321*68711Seric 	/*ILL*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M,
3228078Seric };
3238078Seric 
32465092Seric /* token type table -- it gets modified with $o characters */
325*68711Seric static char TokTypeTab[256] =
32665092Seric {
327*68711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
328*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
329*68711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
330*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
331*68711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
332*68711Seric 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
333*68711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
334*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
335*68711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
336*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
337*68711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
338*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
339*68711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
340*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
341*68711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
342*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
343*68711Seric 
344*68711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
345*68711Seric 	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
346*68711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
347*68711Seric 	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
348*68711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
349*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
350*68711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
351*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
352*68711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
353*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
354*68711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
355*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
356*68711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
357*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
358*68711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
359*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
36065092Seric };
36165092Seric 
362*68711Seric /* token type table for MIME parsing */
363*68711Seric char MimeTokenTab[256] =
364*68711Seric {
365*68711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
366*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
367*68711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
368*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
369*68711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
370*68711Seric 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
371*68711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
372*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
373*68711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
374*68711Seric 	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
375*68711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
376*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
377*68711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
378*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
379*68711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
380*68711Seric 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
38165092Seric 
382*68711Seric     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
383*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
384*68711Seric     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
385*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
386*68711Seric     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
387*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
388*68711Seric     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
389*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
390*68711Seric     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
391*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
392*68711Seric     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
393*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
394*68711Seric     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
395*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
396*68711Seric     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
397*68711Seric 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
398*68711Seric };
39965092Seric 
400*68711Seric 
4018078Seric # define NOCHAR		-1	/* signal nothing in lookahead token */
4028078Seric 
4033149Seric char **
404*68711Seric prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
405297Seric 	char *addr;
40668353Seric 	int delim;
40716914Seric 	char pvpbuf[];
40858333Seric 	char **delimptr;
409*68711Seric 	char *toktab;
410297Seric {
411297Seric 	register char *p;
4128078Seric 	register char *q;
4139346Seric 	register int c;
4143149Seric 	char **avp;
415297Seric 	bool bslashmode;
416297Seric 	int cmntcnt;
4178423Seric 	int anglecnt;
4183149Seric 	char *tok;
4198078Seric 	int state;
4208078Seric 	int newstate;
42159747Seric 	char *saveto = CurEnv->e_to;
4228078Seric 	static char *av[MAXATOM+1];
42365092Seric 	static char firsttime = TRUE;
42456678Seric 	extern int errno;
425297Seric 
42665092Seric 	if (firsttime)
42765092Seric 	{
42865092Seric 		/* initialize the token type table */
42965092Seric 		char obuf[50];
43065092Seric 
43165092Seric 		firsttime = FALSE;
43268529Seric 		expand("\201o", obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
43365092Seric 		strcat(obuf, DELIMCHARS);
43465092Seric 		for (p = obuf; *p != '\0'; p++)
43565092Seric 		{
43665092Seric 			if (TokTypeTab[*p & 0xff] == ATM)
43765092Seric 				TokTypeTab[*p & 0xff] = OPR;
43865092Seric 		}
43965092Seric 	}
440*68711Seric 	if (toktab == NULL)
441*68711Seric 		toktab = TokTypeTab;
44265092Seric 
44315253Seric 	/* make sure error messages don't have garbage on them */
44415253Seric 	errno = 0;
44515253Seric 
44616914Seric 	q = pvpbuf;
4473149Seric 	bslashmode = FALSE;
4487800Seric 	cmntcnt = 0;
4498423Seric 	anglecnt = 0;
4503149Seric 	avp = av;
45156678Seric 	state = ATM;
4528078Seric 	c = NOCHAR;
4538078Seric 	p = addr;
45459747Seric 	CurEnv->e_to = p;
45556764Seric 	if (tTd(22, 11))
456297Seric 	{
4578078Seric 		printf("prescan: ");
4588078Seric 		xputs(p);
45923109Seric 		(void) putchar('\n');
4608078Seric 	}
4618078Seric 
4628078Seric 	do
4638078Seric 	{
4643149Seric 		/* read a token */
4653149Seric 		tok = q;
4668078Seric 		for (;;)
467297Seric 		{
4688078Seric 			/* store away any old lookahead character */
46959277Seric 			if (c != NOCHAR && !bslashmode)
4708078Seric 			{
47115284Seric 				/* see if there is room */
47265066Seric 				if (q >= &pvpbuf[pvpbsize - 5])
4738078Seric 				{
47458151Seric 					usrerr("553 Address too long");
47568526Seric 					if (strlen(addr) > MAXNAME)
47668526Seric 						addr[MAXNAME] = '\0';
47765015Seric 	returnnull:
47858333Seric 					if (delimptr != NULL)
47958333Seric 						*delimptr = p;
48059747Seric 					CurEnv->e_to = saveto;
4818078Seric 					return (NULL);
4828078Seric 				}
48315284Seric 
48415284Seric 				/* squirrel it away */
4858078Seric 				*q++ = c;
4868078Seric 			}
4878078Seric 
4888078Seric 			/* read a new input character */
4898078Seric 			c = *p++;
49057631Seric 			if (c == '\0')
49156764Seric 			{
49256764Seric 				/* diagnose and patch up bad syntax */
49356764Seric 				if (state == QST)
49456764Seric 				{
49564767Seric 					usrerr("653 Unbalanced '\"'");
49656764Seric 					c = '"';
49756764Seric 				}
49856764Seric 				else if (cmntcnt > 0)
49956764Seric 				{
50064767Seric 					usrerr("653 Unbalanced '('");
50156764Seric 					c = ')';
50256764Seric 				}
50356764Seric 				else if (anglecnt > 0)
50456764Seric 				{
50556764Seric 					c = '>';
50664767Seric 					usrerr("653 Unbalanced '<'");
50756764Seric 				}
50856764Seric 				else
50956764Seric 					break;
51015284Seric 
51156764Seric 				p--;
51256764Seric 			}
51357631Seric 			else if (c == delim && anglecnt <= 0 &&
51457631Seric 					cmntcnt <= 0 && state != QST)
51557631Seric 				break;
51656764Seric 
5178078Seric 			if (tTd(22, 101))
5188078Seric 				printf("c=%c, s=%d; ", c, state);
5198078Seric 
5203149Seric 			/* chew up special characters */
5213149Seric 			*q = '\0';
5223149Seric 			if (bslashmode)
5233149Seric 			{
52459105Seric 				bslashmode = FALSE;
52559105Seric 
52624944Seric 				/* kludge \! for naive users */
52758061Seric 				if (cmntcnt > 0)
52859105Seric 				{
52958061Seric 					c = NOCHAR;
53059105Seric 					continue;
53159105Seric 				}
53259105Seric 				else if (c != '!' || state == QST)
53359105Seric 				{
53456678Seric 					*q++ = '\\';
53559105Seric 					continue;
53659105Seric 				}
5373149Seric 			}
53856678Seric 
53956678Seric 			if (c == '\\')
5403149Seric 			{
5413149Seric 				bslashmode = TRUE;
5423149Seric 			}
54356678Seric 			else if (state == QST)
5448514Seric 			{
5458514Seric 				/* do nothing, just avoid next clauses */
5468514Seric 			}
5478078Seric 			else if (c == '(')
5484100Seric 			{
5498078Seric 				cmntcnt++;
5508078Seric 				c = NOCHAR;
5514100Seric 			}
5528078Seric 			else if (c == ')')
5533149Seric 			{
5548078Seric 				if (cmntcnt <= 0)
5553149Seric 				{
55664767Seric 					usrerr("653 Unbalanced ')'");
55763844Seric 					c = NOCHAR;
5583149Seric 				}
5598078Seric 				else
5608078Seric 					cmntcnt--;
5618078Seric 			}
5628078Seric 			else if (cmntcnt > 0)
5638078Seric 				c = NOCHAR;
5648423Seric 			else if (c == '<')
5658423Seric 				anglecnt++;
5668423Seric 			else if (c == '>')
5678423Seric 			{
5688423Seric 				if (anglecnt <= 0)
5698423Seric 				{
57064767Seric 					usrerr("653 Unbalanced '>'");
57163844Seric 					c = NOCHAR;
5728423Seric 				}
57363844Seric 				else
57463844Seric 					anglecnt--;
5758423Seric 			}
57658050Seric 			else if (delim == ' ' && isascii(c) && isspace(c))
57711423Seric 				c = ' ';
5783149Seric 
5798078Seric 			if (c == NOCHAR)
5808078Seric 				continue;
5813149Seric 
5828078Seric 			/* see if this is end of input */
58311405Seric 			if (c == delim && anglecnt <= 0 && state != QST)
5843149Seric 				break;
5853149Seric 
586*68711Seric 			newstate = StateTab[state][toktab[c & 0xff]];
5878078Seric 			if (tTd(22, 101))
5888078Seric 				printf("ns=%02o\n", newstate);
5898078Seric 			state = newstate & TYPE;
590*68711Seric 			if (state == ILL)
591*68711Seric 			{
592*68711Seric 				if (isascii(c) && isprint(c))
593*68711Seric 					usrerr("653 Illegal character %c", c);
594*68711Seric 				else
595*68711Seric 					usrerr("653 Illegal character 0x%02x", c);
596*68711Seric 			}
5978078Seric 			if (bitset(M, newstate))
5988078Seric 				c = NOCHAR;
5998078Seric 			if (bitset(B, newstate))
6004228Seric 				break;
601297Seric 		}
6023149Seric 
6033149Seric 		/* new token */
6048078Seric 		if (tok != q)
6051378Seric 		{
6068078Seric 			*q++ = '\0';
6078078Seric 			if (tTd(22, 36))
608297Seric 			{
6098078Seric 				printf("tok=");
6108078Seric 				xputs(tok);
61123109Seric 				(void) putchar('\n');
612297Seric 			}
6138078Seric 			if (avp >= &av[MAXATOM])
614297Seric 			{
61558151Seric 				syserr("553 prescan: too many tokens");
61665015Seric 				goto returnnull;
617297Seric 			}
61865015Seric 			if (q - tok > MAXNAME)
61965015Seric 			{
62065015Seric 				syserr("553 prescan: token too long");
62165015Seric 				goto returnnull;
62265015Seric 			}
6238078Seric 			*avp++ = tok;
624297Seric 		}
6258423Seric 	} while (c != '\0' && (c != delim || anglecnt > 0));
6263149Seric 	*avp = NULL;
62758333Seric 	p--;
62858333Seric 	if (delimptr != NULL)
62958333Seric 		*delimptr = p;
63056764Seric 	if (tTd(22, 12))
63156764Seric 	{
63256764Seric 		printf("prescan==>");
63356764Seric 		printav(av);
63456764Seric 	}
63559747Seric 	CurEnv->e_to = saveto;
63658546Seric 	if (av[0] == NULL)
63764966Seric 	{
63864966Seric 		if (tTd(22, 1))
63964966Seric 			printf("prescan: null leading token\n");
64058546Seric 		return (NULL);
64164966Seric 	}
64258403Seric 	return (av);
6433149Seric }
6443149Seric /*
6453149Seric **  REWRITE -- apply rewrite rules to token vector.
6463149Seric **
6474476Seric **	This routine is an ordered production system.  Each rewrite
6484476Seric **	rule has a LHS (called the pattern) and a RHS (called the
6494476Seric **	rewrite); 'rwr' points the the current rewrite rule.
6504476Seric **
6514476Seric **	For each rewrite rule, 'avp' points the address vector we
6524476Seric **	are trying to match against, and 'pvp' points to the pattern.
6538058Seric **	If pvp points to a special match value (MATCHZANY, MATCHANY,
6549585Seric **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
6559585Seric **	matched is saved away in the match vector (pointed to by 'mvp').
6564476Seric **
6574476Seric **	When a match between avp & pvp does not match, we try to
6589585Seric **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
6594476Seric **	we must also back out the match in mvp.  If we reach a
6608058Seric **	MATCHANY or MATCHZANY we just extend the match and start
6618058Seric **	over again.
6624476Seric **
6634476Seric **	When we finally match, we rewrite the address vector
6644476Seric **	and try over again.
6654476Seric **
6663149Seric **	Parameters:
6673149Seric **		pvp -- pointer to token vector.
66859027Seric **		ruleset -- the ruleset to use for rewriting.
66965071Seric **		reclevel -- recursion level (to catch loops).
67059027Seric **		e -- the current envelope.
6713149Seric **
6723149Seric **	Returns:
67359084Seric **		A status code.  If EX_TEMPFAIL, higher level code should
67459084Seric **			attempt recovery.
6753149Seric **
6763149Seric **	Side Effects:
6773149Seric **		pvp is modified.
6783149Seric */
6792091Seric 
6803149Seric struct match
6813149Seric {
6824468Seric 	char	**first;	/* first token matched */
6834468Seric 	char	**last;		/* last token matched */
68458825Seric 	char	**pattern;	/* pointer to pattern */
6853149Seric };
6863149Seric 
6874468Seric # define MAXMATCH	9	/* max params per rewrite */
6883149Seric 
68965136Seric # ifndef MAXRULERECURSION
69065136Seric #  define MAXRULERECURSION	50	/* max recursion depth */
69165136Seric # endif
6923149Seric 
69365136Seric 
69459084Seric int
69565071Seric rewrite(pvp, ruleset, reclevel, e)
6963149Seric 	char **pvp;
6974070Seric 	int ruleset;
69865071Seric 	int reclevel;
69959027Seric 	register ENVELOPE *e;
7003149Seric {
7013149Seric 	register char *ap;		/* address pointer */
7023149Seric 	register char *rp;		/* rewrite pointer */
7033149Seric 	register char **avp;		/* address vector pointer */
7043149Seric 	register char **rvp;		/* rewrite vector pointer */
7058058Seric 	register struct match *mlp;	/* cur ptr into mlist */
7068058Seric 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
70758866Seric 	int ruleno;			/* current rule number */
70859084Seric 	int rstat = EX_OK;		/* return status */
70964740Seric 	int loopcount;
71056678Seric 	struct match mlist[MAXMATCH];	/* stores match on LHS */
7113149Seric 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
7123149Seric 
71367262Seric 	if (OpMode == MD_TEST || tTd(21, 1))
7143149Seric 	{
7158959Seric 		printf("rewrite: ruleset %2d   input:", ruleset);
71656678Seric 		printav(pvp);
7173149Seric 	}
71856678Seric 	if (ruleset < 0 || ruleset >= MAXRWSETS)
71956326Seric 	{
72058151Seric 		syserr("554 rewrite: illegal ruleset number %d", ruleset);
72159084Seric 		return EX_CONFIG;
72256326Seric 	}
72365136Seric 	if (reclevel++ > MAXRULERECURSION)
72465071Seric 	{
72565071Seric 		syserr("rewrite: infinite recursion, ruleset %d", ruleset);
72665071Seric 		return EX_CONFIG;
72765071Seric 	}
72856678Seric 	if (pvp == NULL)
72959084Seric 		return EX_USAGE;
73056326Seric 
7313149Seric 	/*
73256678Seric 	**  Run through the list of rewrite rules, applying
73356678Seric 	**	any that match.
7343149Seric 	*/
7353149Seric 
73658866Seric 	ruleno = 1;
73764740Seric 	loopcount = 0;
7384070Seric 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
7393149Seric 	{
7407675Seric 		if (tTd(21, 12))
741297Seric 		{
7428069Seric 			printf("-----trying rule:");
74356678Seric 			printav(rwr->r_lhs);
7443149Seric 		}
7453149Seric 
7463149Seric 		/* try to match on this rule */
7474468Seric 		mlp = mlist;
7488058Seric 		rvp = rwr->r_lhs;
7498058Seric 		avp = pvp;
75058866Seric 		if (++loopcount > 100)
7513149Seric 		{
75258866Seric 			syserr("554 Infinite loop in ruleset %d, rule %d",
75358866Seric 				ruleset, ruleno);
75458866Seric 			if (tTd(21, 1))
75552637Seric 			{
75656678Seric 				printf("workspace: ");
75756678Seric 				printav(pvp);
75852637Seric 			}
75958866Seric 			break;
76058866Seric 		}
76158866Seric 
76258866Seric 		while ((ap = *avp) != NULL || *rvp != NULL)
76358866Seric 		{
7643149Seric 			rp = *rvp;
7658058Seric 			if (tTd(21, 35))
7668058Seric 			{
76758825Seric 				printf("ADVANCE rp=");
76857531Seric 				xputs(rp);
76957532Seric 				printf(", ap=");
7708058Seric 				xputs(ap);
7718069Seric 				printf("\n");
7728058Seric 			}
77356678Seric 			if (rp == NULL)
77456326Seric 			{
7753149Seric 				/* end-of-pattern before end-of-address */
7768058Seric 				goto backup;
77756678Seric 			}
77858173Seric 			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
77958827Seric 			    (*rp & 0377) != MATCHZERO)
78056326Seric 			{
78158825Seric 				/* end-of-input with patterns left */
78258825Seric 				goto backup;
783297Seric 			}
78456326Seric 
78558050Seric 			switch (*rp & 0377)
7868058Seric 			{
78758814Seric 				char buf[MAXLINE];
78856326Seric 
78956678Seric 			  case MATCHCLASS:
79058825Seric 				/* match any phrase in a class */
79158825Seric 				mlp->pattern = rvp;
79258814Seric 				mlp->first = avp;
79358814Seric 	extendclass:
79458825Seric 				ap = *avp;
79558825Seric 				if (ap == NULL)
79658814Seric 					goto backup;
79758814Seric 				mlp->last = avp++;
79858814Seric 				cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
79968199Seric 				if (!wordinclass(buf, rp[1]))
80056326Seric 				{
80158825Seric 					if (tTd(21, 36))
80258825Seric 					{
80358825Seric 						printf("EXTEND  rp=");
80458825Seric 						xputs(rp);
80558825Seric 						printf(", ap=");
80658825Seric 						xputs(ap);
80758825Seric 						printf("\n");
80858825Seric 					}
80958825Seric 					goto extendclass;
81056326Seric 				}
81158825Seric 				if (tTd(21, 36))
81258825Seric 					printf("CLMATCH\n");
81358814Seric 				mlp++;
81458814Seric 				break;
8154060Seric 
81658825Seric 			  case MATCHNCLASS:
81758825Seric 				/* match any token not in a class */
81868199Seric 				if (wordinclass(ap, rp[1]))
81958825Seric 					goto backup;
82058825Seric 
82158825Seric 				/* fall through */
82258825Seric 
82356678Seric 			  case MATCHONE:
82456678Seric 			  case MATCHANY:
82556678Seric 				/* match exactly one token */
82658825Seric 				mlp->pattern = rvp;
82756678Seric 				mlp->first = avp;
82856678Seric 				mlp->last = avp++;
8298058Seric 				mlp++;
83056678Seric 				break;
8318058Seric 
83256678Seric 			  case MATCHZANY:
83356678Seric 				/* match zero or more tokens */
83458825Seric 				mlp->pattern = rvp;
83556678Seric 				mlp->first = avp;
83656678Seric 				mlp->last = avp - 1;
83756678Seric 				mlp++;
83856678Seric 				break;
83956326Seric 
84058827Seric 			  case MATCHZERO:
84158173Seric 				/* match zero tokens */
84258173Seric 				break;
84358173Seric 
84459027Seric 			  case MACRODEXPAND:
84559027Seric 				/*
84659027Seric 				**  Match against run-time macro.
84759027Seric 				**  This algorithm is broken for the
84859027Seric 				**  general case (no recursive macros,
84959027Seric 				**  improper tokenization) but should
85059027Seric 				**  work for the usual cases.
85159027Seric 				*/
85259027Seric 
85359027Seric 				ap = macvalue(rp[1], e);
85459027Seric 				mlp->first = avp;
85559027Seric 				if (tTd(21, 2))
85659027Seric 					printf("rewrite: LHS $&%c => \"%s\"\n",
85759027Seric 						rp[1],
85859027Seric 						ap == NULL ? "(NULL)" : ap);
85959027Seric 
86059027Seric 				if (ap == NULL)
86159027Seric 					break;
86260502Seric 				while (*ap != '\0')
86359027Seric 				{
86459027Seric 					if (*avp == NULL ||
86559027Seric 					    strncasecmp(ap, *avp, strlen(*avp)) != 0)
86659027Seric 					{
86759027Seric 						/* no match */
86859027Seric 						avp = mlp->first;
86959027Seric 						goto backup;
87059027Seric 					}
87159027Seric 					ap += strlen(*avp++);
87259027Seric 				}
87359027Seric 
87459027Seric 				/* match */
87559027Seric 				break;
87659027Seric 
87756678Seric 			  default:
87856678Seric 				/* must have exact match */
87956678Seric 				if (strcasecmp(rp, ap))
8808058Seric 					goto backup;
8814468Seric 				avp++;
88256678Seric 				break;
8833149Seric 			}
8843149Seric 
88556678Seric 			/* successful match on this token */
8863149Seric 			rvp++;
8873149Seric 			continue;
8883149Seric 
88958825Seric 	  backup:
89056678Seric 			/* match failed -- back up */
89158825Seric 			while (--mlp >= mlist)
8923149Seric 			{
89358825Seric 				rvp = mlp->pattern;
89456678Seric 				rp = *rvp;
89558825Seric 				avp = mlp->last + 1;
89658825Seric 				ap = *avp;
89758825Seric 
89858825Seric 				if (tTd(21, 36))
89958825Seric 				{
90058825Seric 					printf("BACKUP  rp=");
90158825Seric 					xputs(rp);
90258825Seric 					printf(", ap=");
90358825Seric 					xputs(ap);
90458825Seric 					printf("\n");
90558825Seric 				}
90658825Seric 
90758825Seric 				if (ap == NULL)
90858825Seric 				{
90958825Seric 					/* run off the end -- back up again */
91058825Seric 					continue;
91158825Seric 				}
91258050Seric 				if ((*rp & 0377) == MATCHANY ||
91358050Seric 				    (*rp & 0377) == MATCHZANY)
9144468Seric 				{
91556678Seric 					/* extend binding and continue */
91658825Seric 					mlp->last = avp++;
91756678Seric 					rvp++;
91858825Seric 					mlp++;
91956678Seric 					break;
9204468Seric 				}
92158825Seric 				if ((*rp & 0377) == MATCHCLASS)
92256678Seric 				{
92358814Seric 					/* extend binding and try again */
92463397Seric 					mlp->last = avp;
92558814Seric 					goto extendclass;
92658814Seric 				}
9273149Seric 			}
9283149Seric 
92958825Seric 			if (mlp < mlist)
93056678Seric 			{
93156678Seric 				/* total failure to match */
93256326Seric 				break;
9333149Seric 			}
934297Seric 		}
9353149Seric 
9363149Seric 		/*
93756678Seric 		**  See if we successfully matched
9383149Seric 		*/
9393149Seric 
94058827Seric 		if (mlp < mlist || *rvp != NULL)
9413149Seric 		{
9429374Seric 			if (tTd(21, 10))
9439374Seric 				printf("----- rule fails\n");
9449374Seric 			rwr = rwr->r_next;
94558866Seric 			ruleno++;
94664740Seric 			loopcount = 0;
9479374Seric 			continue;
9489374Seric 		}
9493149Seric 
9509374Seric 		rvp = rwr->r_rhs;
9519374Seric 		if (tTd(21, 12))
9529374Seric 		{
9539374Seric 			printf("-----rule matches:");
95456678Seric 			printav(rvp);
9559374Seric 		}
9569374Seric 
9579374Seric 		rp = *rvp;
95858050Seric 		if ((*rp & 0377) == CANONUSER)
9599374Seric 		{
9609374Seric 			rvp++;
9619374Seric 			rwr = rwr->r_next;
96258866Seric 			ruleno++;
96364740Seric 			loopcount = 0;
9649374Seric 		}
96558050Seric 		else if ((*rp & 0377) == CANONHOST)
9669374Seric 		{
9679374Seric 			rvp++;
9689374Seric 			rwr = NULL;
9699374Seric 		}
97058050Seric 		else if ((*rp & 0377) == CANONNET)
9719374Seric 			rwr = NULL;
9729374Seric 
9739374Seric 		/* substitute */
9749374Seric 		for (avp = npvp; *rvp != NULL; rvp++)
9759374Seric 		{
9769374Seric 			register struct match *m;
9779374Seric 			register char **pp;
9789374Seric 
9798058Seric 			rp = *rvp;
98058050Seric 			if ((*rp & 0377) == MATCHREPL)
9818058Seric 			{
98216914Seric 				/* substitute from LHS */
98316914Seric 				m = &mlist[rp[1] - '1'];
98456678Seric 				if (m < mlist || m >= mlp)
9859374Seric 				{
98658151Seric 					syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
98756326Seric 						ruleset, rp[1]);
98859084Seric 					return EX_CONFIG;
9899374Seric 				}
99016914Seric 				if (tTd(21, 15))
99116914Seric 				{
99216914Seric 					printf("$%c:", rp[1]);
99316914Seric 					pp = m->first;
99456678Seric 					while (pp <= m->last)
99516914Seric 					{
99616914Seric 						printf(" %x=\"", *pp);
99716914Seric 						(void) fflush(stdout);
99816914Seric 						printf("%s\"", *pp++);
99916914Seric 					}
100016914Seric 					printf("\n");
100116914Seric 				}
10029374Seric 				pp = m->first;
100356678Seric 				while (pp <= m->last)
10043149Seric 				{
100516914Seric 					if (avp >= &npvp[MAXATOM])
100656678Seric 					{
100758151Seric 						syserr("554 rewrite: expansion too long");
100859084Seric 						return EX_DATAERR;
100956678Seric 					}
101016914Seric 					*avp++ = *pp++;
10113149Seric 				}
10123149Seric 			}
101316914Seric 			else
10148226Seric 			{
101516914Seric 				/* vanilla replacement */
10169374Seric 				if (avp >= &npvp[MAXATOM])
101716889Seric 				{
101856678Seric 	toolong:
101958151Seric 					syserr("554 rewrite: expansion too long");
102059084Seric 					return EX_DATAERR;
102116889Seric 				}
102259027Seric 				if ((*rp & 0377) != MACRODEXPAND)
102359027Seric 					*avp++ = rp;
102459027Seric 				else
102559027Seric 				{
102659027Seric 					*avp = macvalue(rp[1], e);
102759027Seric 					if (tTd(21, 2))
102859027Seric 						printf("rewrite: RHS $&%c => \"%s\"\n",
102959027Seric 							rp[1],
103059027Seric 							*avp == NULL ? "(NULL)" : *avp);
103159027Seric 					if (*avp != NULL)
103259027Seric 						avp++;
103359027Seric 				}
10348226Seric 			}
10359374Seric 		}
10369374Seric 		*avp++ = NULL;
103716914Seric 
103816914Seric 		/*
103956678Seric 		**  Check for any hostname/keyword lookups.
104016914Seric 		*/
104116914Seric 
104216914Seric 		for (rvp = npvp; *rvp != NULL; rvp++)
104316914Seric 		{
104456678Seric 			char **hbrvp;
104516914Seric 			char **xpvp;
104616914Seric 			int trsize;
104756678Seric 			char *replac;
104856678Seric 			int endtoken;
104956678Seric 			STAB *map;
105056678Seric 			char *mapname;
105156678Seric 			char **key_rvp;
105256678Seric 			char **arg_rvp;
105356678Seric 			char **default_rvp;
105456678Seric 			char buf[MAXNAME + 1];
105516914Seric 			char *pvpb1[MAXATOM + 1];
105656823Seric 			char *argvect[10];
105717174Seric 			char pvpbuf[PSBUFSIZE];
105864404Seric 			char *nullpvp[1];
105916914Seric 
106058050Seric 			if ((**rvp & 0377) != HOSTBEGIN &&
106158050Seric 			    (**rvp & 0377) != LOOKUPBEGIN)
106216914Seric 				continue;
106316914Seric 
106416914Seric 			/*
106556678Seric 			**  Got a hostname/keyword lookup.
106616914Seric 			**
106716914Seric 			**	This could be optimized fairly easily.
106816914Seric 			*/
106916914Seric 
107016914Seric 			hbrvp = rvp;
107158050Seric 			if ((**rvp & 0377) == HOSTBEGIN)
107256327Seric 			{
107356678Seric 				endtoken = HOSTEND;
107456678Seric 				mapname = "host";
107556327Seric 			}
107656326Seric 			else
107756327Seric 			{
107856678Seric 				endtoken = LOOKUPEND;
107956678Seric 				mapname = *++rvp;
108056327Seric 			}
108156678Seric 			map = stab(mapname, ST_MAP, ST_FIND);
108256678Seric 			if (map == NULL)
108358151Seric 				syserr("554 rewrite: map %s not found", mapname);
108456678Seric 
108556678Seric 			/* extract the match part */
108656678Seric 			key_rvp = ++rvp;
108756823Seric 			default_rvp = NULL;
108856823Seric 			arg_rvp = argvect;
108956823Seric 			xpvp = NULL;
109056823Seric 			replac = pvpbuf;
109158050Seric 			while (*rvp != NULL && (**rvp & 0377) != endtoken)
109253654Seric 			{
109358050Seric 				int nodetype = **rvp & 0377;
109456823Seric 
109556823Seric 				if (nodetype != CANONHOST && nodetype != CANONUSER)
109656678Seric 				{
109756823Seric 					rvp++;
109856823Seric 					continue;
109956823Seric 				}
110056823Seric 
110156823Seric 				*rvp++ = NULL;
110256823Seric 
110356823Seric 				if (xpvp != NULL)
110456823Seric 				{
110558814Seric 					cataddr(xpvp, NULL, replac,
110658082Seric 						&pvpbuf[sizeof pvpbuf] - replac,
110758082Seric 						'\0');
110856823Seric 					*++arg_rvp = replac;
110956823Seric 					replac += strlen(replac) + 1;
111056823Seric 					xpvp = NULL;
111156823Seric 				}
111256823Seric 				switch (nodetype)
111356823Seric 				{
111456678Seric 				  case CANONHOST:
111556823Seric 					xpvp = rvp;
111656678Seric 					break;
111756678Seric 
111856678Seric 				  case CANONUSER:
111956678Seric 					default_rvp = rvp;
112056678Seric 					break;
112156678Seric 				}
112253654Seric 			}
112316914Seric 			if (*rvp != NULL)
112416914Seric 				*rvp++ = NULL;
112556823Seric 			if (xpvp != NULL)
112656823Seric 			{
112758814Seric 				cataddr(xpvp, NULL, replac,
112858082Seric 					&pvpbuf[sizeof pvpbuf] - replac,
112958082Seric 					'\0');
113056823Seric 				*++arg_rvp = replac;
113156823Seric 			}
113256823Seric 			*++arg_rvp = NULL;
113316914Seric 
113416914Seric 			/* save the remainder of the input string */
113516914Seric 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
113616914Seric 			bcopy((char *) rvp, (char *) pvpb1, trsize);
113716914Seric 
113856678Seric 			/* look it up */
113958814Seric 			cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
114056823Seric 			argvect[0] = buf;
114160538Seric 			if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags))
114256836Seric 			{
114359084Seric 				auto int stat = EX_OK;
114456836Seric 
114560215Seric 				/* XXX should try to auto-open the map here */
114660215Seric 
114758796Seric 				if (tTd(60, 1))
114858796Seric 					printf("map_lookup(%s, %s) => ",
114958796Seric 						mapname, buf);
115056836Seric 				replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
115160089Seric 						buf, argvect, &stat);
115258796Seric 				if (tTd(60, 1))
115359084Seric 					printf("%s (%d)\n",
115459084Seric 						replac ? replac : "NOT FOUND",
115559084Seric 						stat);
115659084Seric 
115759084Seric 				/* should recover if stat == EX_TEMPFAIL */
115868706Seric 				if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE)
115968706Seric 				{
116068706Seric 					rstat = EX_TEMPFAIL;
116168708Seric 					if (tTd(50, 1))
116268706Seric 						printf("map_lookup(%s, %s) failed (stat = %d)\n",
116368706Seric 							mapname, buf, stat);
116468706Seric 					if (e->e_message == NULL)
116568706Seric 					{
116668706Seric 						char mbuf[300];
116768706Seric 
116868706Seric 						sprintf(mbuf, "map %s: lookup (%s) failed",
116968706Seric 							mapname, buf);
117068706Seric 						e->e_message = newstr(mbuf);
117168706Seric 					}
117268706Seric 				}
117356836Seric 			}
117453654Seric 			else
117556678Seric 				replac = NULL;
117656678Seric 
117756678Seric 			/* if no replacement, use default */
117856823Seric 			if (replac == NULL && default_rvp != NULL)
117956823Seric 			{
118060089Seric 				/* create the default */
118160089Seric 				cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
118256823Seric 				replac = buf;
118356823Seric 			}
118456823Seric 
118556678Seric 			if (replac == NULL)
118651317Seric 			{
118756823Seric 				xpvp = key_rvp;
118853654Seric 			}
118964404Seric 			else if (*replac == '\0')
119064404Seric 			{
119164404Seric 				/* null replacement */
119264404Seric 				nullpvp[0] = NULL;
119364404Seric 				xpvp = nullpvp;
119464404Seric 			}
119556678Seric 			else
119653654Seric 			{
119756678Seric 				/* scan the new replacement */
119865066Seric 				xpvp = prescan(replac, '\0', pvpbuf,
1199*68711Seric 					       sizeof pvpbuf, NULL, NULL);
120053654Seric 				if (xpvp == NULL)
120151317Seric 				{
120258403Seric 					/* prescan already printed error */
120359084Seric 					return EX_DATAERR;
120456678Seric 				}
120551317Seric 			}
120651317Seric 
120716914Seric 			/* append it to the token list */
120856678Seric 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
120956678Seric 			{
121017174Seric 				*avp++ = newstr(*xpvp);
121116920Seric 				if (avp >= &npvp[MAXATOM])
121216914Seric 					goto toolong;
121317174Seric 			}
121416914Seric 
121516914Seric 			/* restore the old trailing information */
121656678Seric 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
121716920Seric 				if (avp >= &npvp[MAXATOM])
121816914Seric 					goto toolong;
121917174Seric 
122056678Seric 			break;
122116914Seric 		}
122216914Seric 
122316914Seric 		/*
122416914Seric 		**  Check for subroutine calls.
122516914Seric 		*/
122616914Seric 
122768559Seric 		if (*npvp != NULL && (**npvp & 0377) == CALLSUBR)
122868559Seric 		{
122968559Seric 			int stat;
123059084Seric 
123168559Seric 			if (npvp[1] == NULL)
123268559Seric 			{
123368559Seric 				syserr("parseaddr: NULL subroutine call in ruleset %d, rule %d",
123468559Seric 					ruleset, ruleno);
123568559Seric 				*pvp = NULL;
123668559Seric 			}
123768559Seric 			else
123868559Seric 			{
123968559Seric 				int ruleset;
124068559Seric 				STAB *s;
124168385Seric 
124268559Seric 				bcopy((char *) &npvp[2], (char *) pvp,
124368559Seric 					(int) (avp - npvp - 2) * sizeof *avp);
124468559Seric 				if (tTd(21, 3))
124568559Seric 					printf("-----callsubr %s\n", npvp[1]);
124668559Seric 				s = stab(npvp[1], ST_RULESET, ST_FIND);
124768559Seric 				if (s == NULL)
124868559Seric 					ruleset = atoi(npvp[1]);
124968559Seric 				else
125068559Seric 					ruleset = s->s_ruleset;
125168559Seric 				stat = rewrite(pvp, ruleset, reclevel, e);
125268559Seric 				if (rstat == EX_OK || stat == EX_TEMPFAIL)
125368559Seric 					rstat = stat;
125468559Seric 				if (*pvp != NULL && (**pvp & 0377) == CANONNET)
125568559Seric 				rwr = NULL;
125668559Seric 			}
125768559Seric 		}
125868559Seric 		else
125968559Seric 		{
126068559Seric 			bcopy((char *) npvp, (char *) pvp,
126168559Seric 				(int) (avp - npvp) * sizeof *avp);
126268559Seric 		}
12639374Seric 		if (tTd(21, 4))
12649374Seric 		{
12659374Seric 			printf("rewritten as:");
126656678Seric 			printav(pvp);
12679374Seric 		}
1268297Seric 	}
12698069Seric 
127067262Seric 	if (OpMode == MD_TEST || tTd(21, 1))
12718069Seric 	{
12728959Seric 		printf("rewrite: ruleset %2d returns:", ruleset);
127356678Seric 		printav(pvp);
12748069Seric 	}
127559084Seric 
127659084Seric 	return rstat;
12773149Seric }
12783149Seric /*
12793149Seric **  BUILDADDR -- build address from token vector.
12803149Seric **
12813149Seric **	Parameters:
12823149Seric **		tv -- token vector.
12833149Seric **		a -- pointer to address descriptor to fill.
12843149Seric **			If NULL, one will be allocated.
128564284Seric **		flags -- info regarding whether this is a sender or
128664284Seric **			a recipient.
128758966Seric **		e -- the current envelope.
12883149Seric **
12893149Seric **	Returns:
12904279Seric **		NULL if there was an error.
12914279Seric **		'a' otherwise.
12923149Seric **
12933149Seric **	Side Effects:
12943149Seric **		fills in 'a'
12953149Seric */
12963149Seric 
129757249Seric struct errcodes
129857249Seric {
129957249Seric 	char	*ec_name;		/* name of error code */
130057249Seric 	int	ec_code;		/* numeric code */
130157249Seric } ErrorCodes[] =
130257249Seric {
130357249Seric 	"usage",	EX_USAGE,
130457249Seric 	"nouser",	EX_NOUSER,
130557249Seric 	"nohost",	EX_NOHOST,
130657249Seric 	"unavailable",	EX_UNAVAILABLE,
130757249Seric 	"software",	EX_SOFTWARE,
130857249Seric 	"tempfail",	EX_TEMPFAIL,
130957249Seric 	"protocol",	EX_PROTOCOL,
131057249Seric #ifdef EX_CONFIG
131157249Seric 	"config",	EX_CONFIG,
131257249Seric #endif
131357249Seric 	NULL,		EX_UNAVAILABLE,
131457249Seric };
131557249Seric 
131656678Seric ADDRESS *
131764284Seric buildaddr(tv, a, flags, e)
13183149Seric 	register char **tv;
13193149Seric 	register ADDRESS *a;
132064284Seric 	int flags;
132158966Seric 	register ENVELOPE *e;
13223149Seric {
13233149Seric 	struct mailer **mp;
13243149Seric 	register struct mailer *m;
132558008Seric 	char *bp;
132658008Seric 	int spaceleft;
132764306Seric 	static MAILER errormailer;
132864306Seric 	static char *errorargv[] = { "ERROR", NULL };
132968528Seric 	static char buf[MAXNAME + 1];
13303149Seric 
133164791Seric 	if (tTd(24, 5))
133264791Seric 	{
133367693Seric 		printf("buildaddr, flags=%x, tv=", flags);
133464791Seric 		printav(tv);
133564791Seric 	}
133664791Seric 
13373149Seric 	if (a == NULL)
13383149Seric 		a = (ADDRESS *) xalloc(sizeof *a);
133916889Seric 	bzero((char *) a, sizeof *a);
13403149Seric 
134167880Seric 	/* set up default error return flags */
134267963Seric 	a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
134367880Seric 
13443149Seric 	/* figure out what net/mailer to use */
134564306Seric 	if (*tv == NULL || (**tv & 0377) != CANONNET)
13464279Seric 	{
134758151Seric 		syserr("554 buildaddr: no net");
134864306Seric badaddr:
134964306Seric 		a->q_flags |= QBADADDR;
135064306Seric 		a->q_mailer = &errormailer;
135164306Seric 		if (errormailer.m_name == NULL)
135264306Seric 		{
135364306Seric 			/* initialize the bogus mailer */
135464306Seric 			errormailer.m_name = "*error*";
135564306Seric 			errormailer.m_mailer = "ERROR";
135664306Seric 			errormailer.m_argv = errorargv;
135764306Seric 		}
135864306Seric 		return a;
13594279Seric 	}
13603149Seric 	tv++;
136158680Seric 	if (strcasecmp(*tv, "error") == 0)
13624279Seric 	{
136358050Seric 		if ((**++tv & 0377) == CANONHOST)
136410183Seric 		{
136557249Seric 			register struct errcodes *ep;
136657249Seric 
136758050Seric 			if (isascii(**++tv) && isdigit(**tv))
136857249Seric 			{
136957249Seric 				setstat(atoi(*tv));
137057249Seric 			}
137157249Seric 			else
137257249Seric 			{
137357249Seric 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
137457249Seric 					if (strcasecmp(ep->ec_name, *tv) == 0)
137557249Seric 						break;
137657249Seric 				setstat(ep->ec_code);
137757249Seric 			}
137810183Seric 			tv++;
137910183Seric 		}
138064928Seric 		else
138164928Seric 			setstat(EX_UNAVAILABLE);
138258050Seric 		if ((**tv & 0377) != CANONUSER)
138358151Seric 			syserr("554 buildaddr: error: no user");
138458814Seric 		cataddr(++tv, NULL, buf, sizeof buf, ' ');
138558082Seric 		stripquotes(buf);
138664659Seric 		if (isascii(buf[0]) && isdigit(buf[0]) &&
138764659Seric 		    isascii(buf[1]) && isdigit(buf[1]) &&
138864659Seric 		    isascii(buf[2]) && isdigit(buf[2]) &&
138964659Seric 		    buf[3] == ' ')
139064659Seric 		{
139164659Seric 			char fmt[10];
139264659Seric 
139364659Seric 			strncpy(fmt, buf, 3);
139464659Seric 			strcpy(&fmt[3], " %s");
139564659Seric 			usrerr(fmt, buf + 4);
139667786Seric 
139767786Seric 			/*
139867786Seric 			**  If this is a 4xx code and we aren't running
139967786Seric 			**  SMTP on our input, bounce this message;
140067786Seric 			**  otherwise it disappears without a trace.
140167786Seric 			*/
140267786Seric 
140367786Seric 			if (fmt[0] == '4' && OpMode != MD_SMTP &&
140467786Seric 			    OpMode != MD_DAEMON)
140567786Seric 			{
140667786Seric 				e->e_flags |= EF_FATALERRS;
140767786Seric 			}
140864659Seric 		}
140964659Seric 		else
141064659Seric 		{
141166039Seric 			usrerr("553 %s", buf);
141264659Seric 		}
141364306Seric 		goto badaddr;
14144279Seric 	}
141557402Seric 
14164598Seric 	for (mp = Mailer; (m = *mp++) != NULL; )
14173149Seric 	{
141858680Seric 		if (strcasecmp(m->m_name, *tv) == 0)
14193149Seric 			break;
14203149Seric 	}
14213149Seric 	if (m == NULL)
14224279Seric 	{
142358151Seric 		syserr("554 buildaddr: unknown mailer %s", *tv);
142464306Seric 		goto badaddr;
14254279Seric 	}
14264598Seric 	a->q_mailer = m;
14273149Seric 
14283149Seric 	/* figure out what host (if any) */
142956678Seric 	tv++;
143058509Seric 	if ((**tv & 0377) == CANONHOST)
14313149Seric 	{
143258008Seric 		bp = buf;
143358008Seric 		spaceleft = sizeof buf - 1;
143458050Seric 		while (*++tv != NULL && (**tv & 0377) != CANONUSER)
143558008Seric 		{
143658008Seric 			int i = strlen(*tv);
143758008Seric 
143858008Seric 			if (i > spaceleft)
143958008Seric 			{
144058008Seric 				/* out of space for this address */
144158008Seric 				if (spaceleft >= 0)
144258151Seric 					syserr("554 buildaddr: host too long (%.40s...)",
144358008Seric 						buf);
144458008Seric 				i = spaceleft;
144558008Seric 				spaceleft = 0;
144658008Seric 			}
144758008Seric 			if (i <= 0)
144858008Seric 				continue;
144958008Seric 			bcopy(*tv, bp, i);
145058008Seric 			bp += i;
145158008Seric 			spaceleft -= i;
145258008Seric 		}
145358010Seric 		*bp = '\0';
14545704Seric 		a->q_host = newstr(buf);
14553149Seric 	}
145657249Seric 	else
145758509Seric 	{
145858509Seric 		if (!bitnset(M_LOCALMAILER, m->m_flags))
145958509Seric 		{
146058509Seric 			syserr("554 buildaddr: no host");
146164306Seric 			goto badaddr;
146258509Seric 		}
146357249Seric 		a->q_host = NULL;
146458509Seric 	}
14653149Seric 
14663149Seric 	/* figure out the user */
146758050Seric 	if (*tv == NULL || (**tv & 0377) != CANONUSER)
14684279Seric 	{
146958151Seric 		syserr("554 buildaddr: no user");
147064306Seric 		goto badaddr;
14714279Seric 	}
147257402Seric 	tv++;
147351317Seric 
147468098Seric 	if (bitnset(M_CHECKUDB, m->m_flags) && *tv != NULL &&
147568098Seric 	    strcmp(*tv, "@") == 0)
147668098Seric 	{
147768098Seric 		tv++;
147868098Seric 		a->q_flags |= QNOTREMOTE;
147968098Seric 	}
148068098Seric 
148157402Seric 	/* do special mapping for local mailer */
148267472Seric 	if (*tv != NULL)
148357402Seric 	{
148457454Seric 		register char *p = *tv;
148557454Seric 
148657454Seric 		if (*p == '"')
148757454Seric 			p++;
148867472Seric 		if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
148957402Seric 			a->q_mailer = m = ProgMailer;
149067472Seric 		else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
149157402Seric 			a->q_mailer = m = FileMailer;
149267472Seric 		else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
149357402Seric 		{
149457402Seric 			/* may be :include: */
149558814Seric 			cataddr(tv, NULL, buf, sizeof buf, '\0');
149658008Seric 			stripquotes(buf);
149758008Seric 			if (strncasecmp(buf, ":include:", 9) == 0)
149858008Seric 			{
149958008Seric 				/* if :include:, don't need further rewriting */
150057402Seric 				a->q_mailer = m = InclMailer;
150158008Seric 				a->q_user = &buf[9];
150258008Seric 				return (a);
150358008Seric 			}
150457402Seric 		}
150557402Seric 	}
150657402Seric 
150764284Seric 	/* rewrite according recipient mailer rewriting rules */
150864284Seric 	define('h', a->q_host, e);
150964284Seric 	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
151064284Seric 	{
151164284Seric 		/* sender addresses done later */
151265071Seric 		(void) rewrite(tv, 2, 0, e);
151364284Seric 		if (m->m_re_rwset > 0)
151465071Seric 		       (void) rewrite(tv, m->m_re_rwset, 0, e);
151564284Seric 	}
151665071Seric 	(void) rewrite(tv, 4, 0, e);
151719040Seric 
151819040Seric 	/* save the result for the command line/RCPT argument */
151958814Seric 	cataddr(tv, NULL, buf, sizeof buf, '\0');
15203149Seric 	a->q_user = buf;
15213149Seric 
152258670Seric 	/*
152358670Seric 	**  Do mapping to lower case as requested by mailer
152458670Seric 	*/
152558670Seric 
152658670Seric 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
152758670Seric 		makelower(a->q_host);
152858670Seric 	if (!bitnset(M_USR_UPPER, m->m_flags))
152958670Seric 		makelower(a->q_user);
153058670Seric 
15313149Seric 	return (a);
15323149Seric }
15333188Seric /*
15344228Seric **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
15354228Seric **
15364228Seric **	Parameters:
15374228Seric **		pvp -- parameter vector to rebuild.
153858814Seric **		evp -- last parameter to include.  Can be NULL to
153958814Seric **			use entire pvp.
15404228Seric **		buf -- buffer to build the string into.
15414228Seric **		sz -- size of buf.
154258082Seric **		spacesub -- the space separator character; if null,
154358082Seric **			use SpaceSub.
15444228Seric **
15454228Seric **	Returns:
15464228Seric **		none.
15474228Seric **
15484228Seric **	Side Effects:
15494228Seric **		Destroys buf.
15504228Seric */
15514228Seric 
155258814Seric cataddr(pvp, evp, buf, sz, spacesub)
15534228Seric 	char **pvp;
155458814Seric 	char **evp;
15554228Seric 	char *buf;
15564228Seric 	register int sz;
155758082Seric 	char spacesub;
15584228Seric {
15594228Seric 	bool oatomtok = FALSE;
156056678Seric 	bool natomtok = FALSE;
15614228Seric 	register int i;
15624228Seric 	register char *p;
15634228Seric 
156458082Seric 	if (spacesub == '\0')
156558082Seric 		spacesub = SpaceSub;
156658082Seric 
15678423Seric 	if (pvp == NULL)
15688423Seric 	{
156923109Seric 		(void) strcpy(buf, "");
15708423Seric 		return;
15718423Seric 	}
15724228Seric 	p = buf;
157311156Seric 	sz -= 2;
15744228Seric 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
15754228Seric 	{
1576*68711Seric 		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
15774228Seric 		if (oatomtok && natomtok)
157858082Seric 			*p++ = spacesub;
15794228Seric 		(void) strcpy(p, *pvp);
15804228Seric 		oatomtok = natomtok;
15814228Seric 		p += i;
158211156Seric 		sz -= i + 1;
158358814Seric 		if (pvp++ == evp)
158458814Seric 			break;
15854228Seric 	}
15864228Seric 	*p = '\0';
15874228Seric }
15884228Seric /*
15893188Seric **  SAMEADDR -- Determine if two addresses are the same
15903188Seric **
15913188Seric **	This is not just a straight comparison -- if the mailer doesn't
15923188Seric **	care about the host we just ignore it, etc.
15933188Seric **
15943188Seric **	Parameters:
15953188Seric **		a, b -- pointers to the internal forms to compare.
15963188Seric **
15973188Seric **	Returns:
15983188Seric **		TRUE -- they represent the same mailbox.
15993188Seric **		FALSE -- they don't.
16003188Seric **
16013188Seric **	Side Effects:
16023188Seric **		none.
16033188Seric */
16043188Seric 
16053188Seric bool
16069374Seric sameaddr(a, b)
16073188Seric 	register ADDRESS *a;
16083188Seric 	register ADDRESS *b;
16093188Seric {
161065093Seric 	register ADDRESS *ca, *cb;
161165093Seric 
16123188Seric 	/* if they don't have the same mailer, forget it */
16133188Seric 	if (a->q_mailer != b->q_mailer)
16143188Seric 		return (FALSE);
16153188Seric 
16163188Seric 	/* if the user isn't the same, we can drop out */
161756678Seric 	if (strcmp(a->q_user, b->q_user) != 0)
16183188Seric 		return (FALSE);
16193188Seric 
162065093Seric 	/* if we have good uids for both but they differ, these are different */
162165379Seric 	if (a->q_mailer == ProgMailer)
162265379Seric 	{
162365379Seric 		ca = getctladdr(a);
162465379Seric 		cb = getctladdr(b);
162565379Seric 		if (ca != NULL && cb != NULL &&
162665379Seric 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
162765379Seric 		    ca->q_uid != cb->q_uid)
162865379Seric 			return (FALSE);
162965379Seric 	}
163058438Seric 
163158509Seric 	/* otherwise compare hosts (but be careful for NULL ptrs) */
163258509Seric 	if (a->q_host == b->q_host)
163358509Seric 	{
163458509Seric 		/* probably both null pointers */
16353188Seric 		return (TRUE);
163658509Seric 	}
16373188Seric 	if (a->q_host == NULL || b->q_host == NULL)
163858509Seric 	{
163958509Seric 		/* only one is a null pointer */
16403188Seric 		return (FALSE);
164158509Seric 	}
164256678Seric 	if (strcmp(a->q_host, b->q_host) != 0)
16433188Seric 		return (FALSE);
16443188Seric 
16453188Seric 	return (TRUE);
16463188Seric }
16473234Seric /*
16483234Seric **  PRINTADDR -- print address (for debugging)
16493234Seric **
16503234Seric **	Parameters:
16513234Seric **		a -- the address to print
16523234Seric **		follow -- follow the q_next chain.
16533234Seric **
16543234Seric **	Returns:
16553234Seric **		none.
16563234Seric **
16573234Seric **	Side Effects:
16583234Seric **		none.
16593234Seric */
16603234Seric 
166167994Seric struct qflags
166267994Seric {
166367994Seric 	char	*qf_name;
166467994Seric 	u_long	qf_bit;
166567994Seric };
166667994Seric 
166767994Seric struct qflags	AddressFlags[] =
166867994Seric {
166967994Seric 	"QDONTSEND",		QDONTSEND,
167067994Seric 	"QBADADDR",		QBADADDR,
167167994Seric 	"QGOODUID",		QGOODUID,
167267994Seric 	"QPRIMARY",		QPRIMARY,
167367994Seric 	"QQUEUEUP",		QQUEUEUP,
167467994Seric 	"QSENT",		QSENT,
167567994Seric 	"QNOTREMOTE",		QNOTREMOTE,
167667994Seric 	"QSELFREF",		QSELFREF,
167767994Seric 	"QVERIFIED",		QVERIFIED,
167867994Seric 	"QREPORT",		QREPORT,
167967994Seric 	"QBOGUSSHELL",		QBOGUSSHELL,
168067994Seric 	"QUNSAFEADDR",		QUNSAFEADDR,
168167994Seric 	"QPINGONSUCCESS",	QPINGONSUCCESS,
168267994Seric 	"QPINGONFAILURE",	QPINGONFAILURE,
168367994Seric 	"QPINGONDELAY",		QPINGONDELAY,
168468595Seric 	"QHASNOTIFY",		QHASNOTIFY,
168567994Seric 	"QRELAYED",		QRELAYED,
168668603Seric 	"QEXPLODED",		QEXPLODED,
168768603Seric 	"QTHISPASS",		QTHISPASS,
168867994Seric 	NULL
168967994Seric };
169067994Seric 
169168433Seric void
16923234Seric printaddr(a, follow)
16933234Seric 	register ADDRESS *a;
16943234Seric 	bool follow;
16953234Seric {
169657731Seric 	register MAILER *m;
169757731Seric 	MAILER pseudomailer;
169867994Seric 	register struct qflags *qfp;
169967994Seric 	bool firstone;
17005001Seric 
170167994Seric 	if (a == NULL)
170267994Seric 	{
170367994Seric 		printf("[NULL]\n");
170467994Seric 		return;
170567994Seric 	}
170667994Seric 
17073234Seric 	while (a != NULL)
17083234Seric 	{
17094443Seric 		printf("%x=", a);
17104085Seric 		(void) fflush(stdout);
171157731Seric 
171257731Seric 		/* find the mailer -- carefully */
171357731Seric 		m = a->q_mailer;
171457731Seric 		if (m == NULL)
171557731Seric 		{
171657731Seric 			m = &pseudomailer;
171757731Seric 			m->m_mno = -1;
171857731Seric 			m->m_name = "NULL";
171957731Seric 		}
172057731Seric 
172168603Seric 		printf("%s:\n\tmailer %d (%s), host `%s'\n",
172257731Seric 		       a->q_paddr, m->m_mno, m->m_name,
172368603Seric 		       a->q_host == NULL ? "<null>" : a->q_host);
172468603Seric 		printf("\tuser `%s', ruser `%s'\n",
172568603Seric 		       a->q_user,
172667172Seric 		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
172767994Seric 		printf("\tnext=%x, alias %x, uid %d, gid %d\n",
172867994Seric 		       a->q_next, a->q_alias, a->q_uid, a->q_gid);
172967994Seric 		printf("\tflags=%lx<", a->q_flags);
173067994Seric 		firstone = TRUE;
173167994Seric 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
173267994Seric 		{
173367994Seric 			if (!bitset(qfp->qf_bit, a->q_flags))
173467994Seric 				continue;
173567994Seric 			if (!firstone)
173667994Seric 				printf(",");
173767994Seric 			firstone = FALSE;
173867994Seric 			printf("%s", qfp->qf_name);
173967994Seric 		}
174067994Seric 		printf(">\n");
174159269Seric 		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
174259269Seric 		       a->q_owner == NULL ? "(none)" : a->q_owner,
174363756Seric 		       a->q_home == NULL ? "(none)" : a->q_home,
174463756Seric 		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
174568228Seric 		printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n",
174667987Seric 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
174767987Seric 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
174867990Seric 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
17494996Seric 
17503234Seric 		if (!follow)
17513234Seric 			return;
17524996Seric 		a = a->q_next;
17533234Seric 	}
17543234Seric }
175567939Seric /*
175667939Seric **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
175767939Seric **
175867939Seric **	Parameters:
175967939Seric **		a -- pointer to the address
176067939Seric **
176167939Seric **	Returns:
176267939Seric **		TRUE -- if this address is "empty" (i.e., no one should
176367939Seric **			ever generate replies to it.
176467939Seric **		FALSE -- if it is a "regular" (read: replyable) address.
176567939Seric */
17664317Seric 
176767939Seric bool
176867939Seric emptyaddr(a)
176967939Seric 	register ADDRESS *a;
177067939Seric {
177167939Seric 	return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
177267939Seric }
17737682Seric /*
17747682Seric **  REMOTENAME -- return the name relative to the current mailer
17757682Seric **
17767682Seric **	Parameters:
17777682Seric **		name -- the name to translate.
17788069Seric **		m -- the mailer that we want to do rewriting relative
17798069Seric **			to.
178059163Seric **		flags -- fine tune operations.
178159163Seric **		pstat -- pointer to status word.
178258020Seric **		e -- the current envelope.
17837682Seric **
17847682Seric **	Returns:
17857682Seric **		the text string representing this address relative to
17867682Seric **			the receiving mailer.
17877682Seric **
17887682Seric **	Side Effects:
17897682Seric **		none.
17907682Seric **
17917682Seric **	Warnings:
17927682Seric **		The text string returned is tucked away locally;
17937682Seric **			copy it if you intend to save it.
17947682Seric */
17957682Seric 
17967682Seric char *
179759163Seric remotename(name, m, flags, pstat, e)
17987682Seric 	char *name;
179956678Seric 	struct mailer *m;
180059163Seric 	int flags;
180159163Seric 	int *pstat;
180256678Seric 	register ENVELOPE *e;
18037682Seric {
18048069Seric 	register char **pvp;
18058069Seric 	char *fancy;
180656678Seric 	char *oldg = macvalue('g', e);
180758020Seric 	int rwset;
180868528Seric 	static char buf[MAXNAME + 1];
180968528Seric 	char lbuf[MAXNAME + 1];
181016914Seric 	char pvpbuf[PSBUFSIZE];
181156678Seric 	extern char *crackaddr();
18127682Seric 
18137755Seric 	if (tTd(12, 1))
18147755Seric 		printf("remotename(%s)\n", name);
18157755Seric 
181610177Seric 	/* don't do anything if we are tagging it as special */
181759163Seric 	if (bitset(RF_SENDERADDR, flags))
181859163Seric 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
181959163Seric 						     : m->m_se_rwset;
182058020Seric 	else
182159163Seric 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
182259163Seric 						     : m->m_re_rwset;
182358020Seric 	if (rwset < 0)
182410177Seric 		return (name);
182510177Seric 
18267682Seric 	/*
18278181Seric 	**  Do a heuristic crack of this name to extract any comment info.
18288181Seric 	**	This will leave the name as a comment and a $g macro.
18297889Seric 	*/
18307889Seric 
183159163Seric 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
183258050Seric 		fancy = "\201g";
183310310Seric 	else
183410310Seric 		fancy = crackaddr(name);
18357889Seric 
18368181Seric 	/*
18378181Seric 	**  Turn the name into canonical form.
18388181Seric 	**	Normally this will be RFC 822 style, i.e., "user@domain".
18398181Seric 	**	If this only resolves to "user", and the "C" flag is
18408181Seric 	**	specified in the sending mailer, then the sender's
18418181Seric 	**	domain will be appended.
18428181Seric 	*/
18438181Seric 
1844*68711Seric 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
18457889Seric 	if (pvp == NULL)
18467889Seric 		return (name);
184765071Seric 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
184859163Seric 		*pstat = EX_TEMPFAIL;
184959163Seric 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
18508181Seric 	{
18518181Seric 		/* append from domain to this address */
18528181Seric 		register char **pxp = pvp;
18538181Seric 
18549594Seric 		/* see if there is an "@domain" in the current name */
18558181Seric 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
18568181Seric 			pxp++;
18578181Seric 		if (*pxp == NULL)
18588181Seric 		{
18599594Seric 			/* no.... append the "@domain" from the sender */
186056678Seric 			register char **qxq = e->e_fromdomain;
18618181Seric 
18629594Seric 			while ((*pxp++ = *qxq++) != NULL)
18639594Seric 				continue;
186465071Seric 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
186559163Seric 				*pstat = EX_TEMPFAIL;
18668181Seric 		}
18678181Seric 	}
18688181Seric 
18698181Seric 	/*
18708959Seric 	**  Do more specific rewriting.
187156678Seric 	**	Rewrite using ruleset 1 or 2 depending on whether this is
187256678Seric 	**		a sender address or not.
18738181Seric 	**	Then run it through any receiving-mailer-specific rulesets.
18748181Seric 	*/
18758181Seric 
187659163Seric 	if (bitset(RF_SENDERADDR, flags))
187759541Seric 	{
187865071Seric 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
187959163Seric 			*pstat = EX_TEMPFAIL;
188059541Seric 	}
18818069Seric 	else
188259541Seric 	{
188365071Seric 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
188459163Seric 			*pstat = EX_TEMPFAIL;
188559541Seric 	}
188658020Seric 	if (rwset > 0)
188759541Seric 	{
188865071Seric 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
188959163Seric 			*pstat = EX_TEMPFAIL;
189059541Seric 	}
18917682Seric 
18928181Seric 	/*
18938959Seric 	**  Do any final sanitation the address may require.
18948959Seric 	**	This will normally be used to turn internal forms
18958959Seric 	**	(e.g., user@host.LOCAL) into external form.  This
18968959Seric 	**	may be used as a default to the above rules.
18978959Seric 	*/
18988959Seric 
189965071Seric 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
190059163Seric 		*pstat = EX_TEMPFAIL;
19018959Seric 
19028959Seric 	/*
19038181Seric 	**  Now restore the comment information we had at the beginning.
19048181Seric 	*/
19058181Seric 
190658825Seric 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
190756678Seric 	define('g', lbuf, e);
190865494Seric 
190965494Seric 	/* need to make sure route-addrs have <angle brackets> */
191065494Seric 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
191168529Seric 		expand("<\201g>", buf, sizeof buf, e);
191265494Seric 	else
191368529Seric 		expand(fancy, buf, sizeof buf, e);
191465494Seric 
191556678Seric 	define('g', oldg, e);
19167682Seric 
19177682Seric 	if (tTd(12, 1))
19187755Seric 		printf("remotename => `%s'\n", buf);
19197682Seric 	return (buf);
19207682Seric }
192151317Seric /*
192256678Seric **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
192351317Seric **
192451317Seric **	Parameters:
192556678Seric **		a -- the address to map (but just the user name part).
192656678Seric **		sendq -- the sendq in which to install any replacement
192756678Seric **			addresses.
192867982Seric **		aliaslevel -- the alias nesting depth.
192967982Seric **		e -- the envelope.
193051317Seric **
193151317Seric **	Returns:
193251317Seric **		none.
193351317Seric */
193451317Seric 
193567982Seric maplocaluser(a, sendq, aliaslevel, e)
193656678Seric 	register ADDRESS *a;
193756678Seric 	ADDRESS **sendq;
193867982Seric 	int aliaslevel;
193956678Seric 	ENVELOPE *e;
194051317Seric {
194156678Seric 	register char **pvp;
194256678Seric 	register ADDRESS *a1 = NULL;
194358333Seric 	auto char *delimptr;
194456678Seric 	char pvpbuf[PSBUFSIZE];
194551317Seric 
194656678Seric 	if (tTd(29, 1))
194756678Seric 	{
194856678Seric 		printf("maplocaluser: ");
194956678Seric 		printaddr(a, FALSE);
195056678Seric 	}
1951*68711Seric 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
195256678Seric 	if (pvp == NULL)
195356678Seric 		return;
195451317Seric 
195565071Seric 	(void) rewrite(pvp, 5, 0, e);
195658050Seric 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
195756678Seric 		return;
195851317Seric 
195956678Seric 	/* if non-null, mailer destination specified -- has it changed? */
196064284Seric 	a1 = buildaddr(pvp, NULL, 0, e);
196156678Seric 	if (a1 == NULL || sameaddr(a, a1))
196256678Seric 		return;
196351317Seric 
196456678Seric 	/* mark old address as dead; insert new address */
196556678Seric 	a->q_flags |= QDONTSEND;
196657731Seric 	if (tTd(29, 5))
196757731Seric 	{
196857731Seric 		printf("maplocaluser: QDONTSEND ");
196957731Seric 		printaddr(a, FALSE);
197057731Seric 	}
197156678Seric 	a1->q_alias = a;
197264348Seric 	allocaddr(a1, RF_COPYALL, NULL);
197367982Seric 	(void) recipient(a1, sendq, aliaslevel, e);
197451317Seric }
197558802Seric /*
197658802Seric **  DEQUOTE_INIT -- initialize dequote map
197758802Seric **
197858802Seric **	This is a no-op.
197958802Seric **
198058802Seric **	Parameters:
198158802Seric **		map -- the internal map structure.
198258802Seric **		args -- arguments.
198358802Seric **
198458802Seric **	Returns:
198558802Seric **		TRUE.
198658802Seric */
198758802Seric 
198858802Seric bool
198960219Seric dequote_init(map, args)
199058802Seric 	MAP *map;
199158802Seric 	char *args;
199258802Seric {
199358805Seric 	register char *p = args;
199458805Seric 
199558805Seric 	for (;;)
199658805Seric 	{
199758805Seric 		while (isascii(*p) && isspace(*p))
199858805Seric 			p++;
199958805Seric 		if (*p != '-')
200058805Seric 			break;
200158805Seric 		switch (*++p)
200258805Seric 		{
200358805Seric 		  case 'a':
200458805Seric 			map->map_app = ++p;
200558805Seric 			break;
200667824Seric 
200767824Seric 		  case 's':
200867824Seric 			map->map_coldelim = *++p;
200967824Seric 			break;
201058805Seric 		}
201158805Seric 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
201258805Seric 			p++;
201358805Seric 		if (*p != '\0')
201458805Seric 			*p = '\0';
201558805Seric 	}
201658805Seric 	if (map->map_app != NULL)
201758805Seric 		map->map_app = newstr(map->map_app);
201858805Seric 
201958802Seric 	return TRUE;
202058802Seric }
202158802Seric /*
202258802Seric **  DEQUOTE_MAP -- unquote an address
202358802Seric **
202458802Seric **	Parameters:
202558802Seric **		map -- the internal map structure (ignored).
202660089Seric **		name -- the name to dequote.
202758802Seric **		av -- arguments (ignored).
202859084Seric **		statp -- pointer to status out-parameter.
202958802Seric **
203058802Seric **	Returns:
203158802Seric **		NULL -- if there were no quotes, or if the resulting
203258802Seric **			unquoted buffer would not be acceptable to prescan.
203358802Seric **		else -- The dequoted buffer.
203458802Seric */
203558802Seric 
203658802Seric char *
203760089Seric dequote_map(map, name, av, statp)
203858802Seric 	MAP *map;
203960089Seric 	char *name;
204058802Seric 	char **av;
204159084Seric 	int *statp;
204258802Seric {
204358802Seric 	register char *p;
204458802Seric 	register char *q;
204558802Seric 	register char c;
204667824Seric 	int anglecnt = 0;
204767824Seric 	int cmntcnt = 0;
204867824Seric 	int quotecnt = 0;
204967824Seric 	int spacecnt = 0;
205067824Seric 	bool quotemode = FALSE;
205167824Seric 	bool bslashmode = FALSE;
205267824Seric 	char spacesub = map->map_coldelim;
205358802Seric 
205460089Seric 	for (p = q = name; (c = *p++) != '\0'; )
205558802Seric 	{
205658802Seric 		if (bslashmode)
205758802Seric 		{
205858802Seric 			bslashmode = FALSE;
205958802Seric 			*q++ = c;
206058802Seric 			continue;
206158802Seric 		}
206258802Seric 
206367824Seric 		if (c == ' ' && spacesub != '\0')
206467824Seric 			c = spacesub;
206567764Seric 
206658802Seric 		switch (c)
206758802Seric 		{
206858802Seric 		  case '\\':
206958802Seric 			bslashmode = TRUE;
207058802Seric 			break;
207158802Seric 
207258802Seric 		  case '(':
207358802Seric 			cmntcnt++;
207458802Seric 			break;
207558802Seric 
207658802Seric 		  case ')':
207758802Seric 			if (cmntcnt-- <= 0)
207858802Seric 				return NULL;
207958802Seric 			break;
208059089Seric 
208159089Seric 		  case ' ':
208259089Seric 			spacecnt++;
208359089Seric 			break;
208458802Seric 		}
208558802Seric 
208658802Seric 		if (cmntcnt > 0)
208758802Seric 		{
208858802Seric 			*q++ = c;
208958802Seric 			continue;
209058802Seric 		}
209158802Seric 
209258802Seric 		switch (c)
209358802Seric 		{
209458802Seric 		  case '"':
209558802Seric 			quotemode = !quotemode;
209658802Seric 			quotecnt++;
209758802Seric 			continue;
209858802Seric 
209958802Seric 		  case '<':
210058802Seric 			anglecnt++;
210158802Seric 			break;
210258802Seric 
210358802Seric 		  case '>':
210458802Seric 			if (anglecnt-- <= 0)
210558802Seric 				return NULL;
210658802Seric 			break;
210758802Seric 		}
210858802Seric 		*q++ = c;
210958802Seric 	}
211058802Seric 
211158802Seric 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
211259089Seric 	    quotemode || quotecnt <= 0 || spacecnt != 0)
211358802Seric 		return NULL;
211458802Seric 	*q++ = '\0';
211560089Seric 	return name;
211658802Seric }
2117