xref: /freebsd/contrib/sendmail/src/parseaddr.c (revision 11afcc8f9f96d657b8e6f7547c02c1957331fc96)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)parseaddr.c	8.153 (Berkeley) 6/24/98";
15 #endif /* not lint */
16 
17 # include "sendmail.h"
18 
19 /*
20 **  PARSEADDR -- Parse an address
21 **
22 **	Parses an address and breaks it up into three parts: a
23 **	net to transmit the message on, the host to transmit it
24 **	to, and a user on that host.  These are loaded into an
25 **	ADDRESS header with the values squirreled away if necessary.
26 **	The "user" part may not be a real user; the process may
27 **	just reoccur on that machine.  For example, on a machine
28 **	with an arpanet connection, the address
29 **		csvax.bill@berkeley
30 **	will break up to a "user" of 'csvax.bill' and a host
31 **	of 'berkeley' -- to be transmitted over the arpanet.
32 **
33 **	Parameters:
34 **		addr -- the address to parse.
35 **		a -- a pointer to the address descriptor buffer.
36 **			If NULL, a header will be created.
37 **		flags -- describe detail for parsing.  See RF_ definitions
38 **			in sendmail.h.
39 **		delim -- the character to terminate the address, passed
40 **			to prescan.
41 **		delimptr -- if non-NULL, set to the location of the
42 **			delim character that was found.
43 **		e -- the envelope that will contain this address.
44 **
45 **	Returns:
46 **		A pointer to the address descriptor header (`a' if
47 **			`a' is non-NULL).
48 **		NULL on error.
49 **
50 **	Side Effects:
51 **		none
52 */
53 
54 /* following delimiters are inherent to the internal algorithms */
55 # define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */
56 
57 ADDRESS *
58 parseaddr(addr, a, flags, delim, delimptr, e)
59 	char *addr;
60 	register ADDRESS *a;
61 	int flags;
62 	int delim;
63 	char **delimptr;
64 	register ENVELOPE *e;
65 {
66 	register char **pvp;
67 	auto char *delimptrbuf;
68 	bool queueup;
69 	char pvpbuf[PSBUFSIZE];
70 	extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
71 	extern bool invalidaddr __P((char *, char *));
72 	extern void allocaddr __P((ADDRESS *, int, char *));
73 
74 	/*
75 	**  Initialize and prescan address.
76 	*/
77 
78 	e->e_to = addr;
79 	if (tTd(20, 1))
80 		printf("\n--parseaddr(%s)\n", addr);
81 
82 	if (delimptr == NULL)
83 		delimptr = &delimptrbuf;
84 
85 	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
86 	if (pvp == NULL)
87 	{
88 		if (tTd(20, 1))
89 			printf("parseaddr-->NULL\n");
90 		return (NULL);
91 	}
92 
93 	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
94 	{
95 		if (tTd(20, 1))
96 			printf("parseaddr-->bad address\n");
97 		return NULL;
98 	}
99 
100 	/*
101 	**  Save addr if we are going to have to.
102 	**
103 	**	We have to do this early because there is a chance that
104 	**	the map lookups in the rewriting rules could clobber
105 	**	static memory somewhere.
106 	*/
107 
108 	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
109 	{
110 		char savec = **delimptr;
111 
112 		if (savec != '\0')
113 			**delimptr = '\0';
114 		e->e_to = addr = newstr(addr);
115 		if (savec != '\0')
116 			**delimptr = savec;
117 	}
118 
119 	/*
120 	**  Apply rewriting rules.
121 	**	Ruleset 0 does basic parsing.  It must resolve.
122 	*/
123 
124 	queueup = FALSE;
125 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
126 		queueup = TRUE;
127 	if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
128 		queueup = TRUE;
129 
130 
131 	/*
132 	**  Build canonical address from pvp.
133 	*/
134 
135 	a = buildaddr(pvp, a, flags, e);
136 
137 	/*
138 	**  Make local copies of the host & user and then
139 	**  transport them out.
140 	*/
141 
142 	allocaddr(a, flags, addr);
143 	if (bitset(QBADADDR, a->q_flags))
144 		return a;
145 
146 	/*
147 	**  If there was a parsing failure, mark it for queueing.
148 	*/
149 
150 	if (queueup && OpMode != MD_INITALIAS)
151 	{
152 		char *msg = "Transient parse error -- message queued for future delivery";
153 
154 		if (e->e_sendmode == SM_DEFER)
155 			msg = "Deferring message until queue run";
156 		if (tTd(20, 1))
157 			printf("parseaddr: queuing message\n");
158 		message(msg);
159 		if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
160 			e->e_message = newstr(msg);
161 		a->q_flags |= QQUEUEUP;
162 		a->q_status = "4.4.3";
163 	}
164 
165 	/*
166 	**  Compute return value.
167 	*/
168 
169 	if (tTd(20, 1))
170 	{
171 		printf("parseaddr-->");
172 		printaddr(a, FALSE);
173 	}
174 
175 	return (a);
176 }
177 /*
178 **  INVALIDADDR -- check for address containing meta-characters
179 **
180 **	Parameters:
181 **		addr -- the address to check.
182 **
183 **	Returns:
184 **		TRUE -- if the address has any "wierd" characters
185 **		FALSE -- otherwise.
186 */
187 
188 bool
189 invalidaddr(addr, delimptr)
190 	register char *addr;
191 	char *delimptr;
192 {
193 	char savedelim = '\0';
194 
195 	if (delimptr != NULL)
196 	{
197 		savedelim = *delimptr;
198 		if (savedelim != '\0')
199 			*delimptr = '\0';
200 	}
201 	if (strlen(addr) > TOBUFSIZE - 2)
202 	{
203 		usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2);
204 		goto failure;
205 	}
206 	for (; *addr != '\0'; addr++)
207 	{
208 		if ((*addr & 0340) == 0200)
209 			break;
210 	}
211 	if (*addr == '\0')
212 	{
213 		if (delimptr != NULL && savedelim != '\0')
214 			*delimptr = savedelim;
215 		return FALSE;
216 	}
217 	setstat(EX_USAGE);
218 	usrerr("553 Address contained invalid control characters");
219 failure:
220 	if (delimptr != NULL && savedelim != '\0')
221 		*delimptr = savedelim;
222 	return TRUE;
223 }
224 /*
225 **  ALLOCADDR -- do local allocations of address on demand.
226 **
227 **	Also lowercases the host name if requested.
228 **
229 **	Parameters:
230 **		a -- the address to reallocate.
231 **		flags -- the copy flag (see RF_ definitions in sendmail.h
232 **			for a description).
233 **		paddr -- the printname of the address.
234 **
235 **	Returns:
236 **		none.
237 **
238 **	Side Effects:
239 **		Copies portions of a into local buffers as requested.
240 */
241 
242 void
243 allocaddr(a, flags, paddr)
244 	register ADDRESS *a;
245 	int flags;
246 	char *paddr;
247 {
248 	if (tTd(24, 4))
249 		printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
250 
251 	a->q_paddr = paddr;
252 
253 	if (a->q_user == NULL)
254 		a->q_user = "";
255 	if (a->q_host == NULL)
256 		a->q_host = "";
257 
258 	if (bitset(RF_COPYPARSE, flags))
259 	{
260 		a->q_host = newstr(a->q_host);
261 		if (a->q_user != a->q_paddr)
262 			a->q_user = newstr(a->q_user);
263 	}
264 
265 	if (a->q_paddr == NULL)
266 		a->q_paddr = a->q_user;
267 }
268 /*
269 **  PRESCAN -- Prescan name and make it canonical
270 **
271 **	Scans a name and turns it into a set of tokens.  This process
272 **	deletes blanks and comments (in parentheses).
273 **
274 **	This routine knows about quoted strings and angle brackets.
275 **
276 **	There are certain subtleties to this routine.  The one that
277 **	comes to mind now is that backslashes on the ends of names
278 **	are silently stripped off; this is intentional.  The problem
279 **	is that some versions of sndmsg (like at LBL) set the kill
280 **	character to something other than @ when reading addresses;
281 **	so people type "csvax.eric\@berkeley" -- which screws up the
282 **	berknet mailer.
283 **
284 **	Parameters:
285 **		addr -- the name to chomp.
286 **		delim -- the delimiter for the address, normally
287 **			'\0' or ','; \0 is accepted in any case.
288 **			If '\t' then we are reading the .cf file.
289 **		pvpbuf -- place to put the saved text -- note that
290 **			the pointers are static.
291 **		pvpbsize -- size of pvpbuf.
292 **		delimptr -- if non-NULL, set to the location of the
293 **			terminating delimiter.
294 **		toktab -- if set, a token table to use for parsing.
295 **			If NULL, use the default table.
296 **
297 **	Returns:
298 **		A pointer to a vector of tokens.
299 **		NULL on error.
300 */
301 
302 /* states and character types */
303 # define OPR		0	/* operator */
304 # define ATM		1	/* atom */
305 # define QST		2	/* in quoted string */
306 # define SPC		3	/* chewing up spaces */
307 # define ONE		4	/* pick up one character */
308 # define ILL		5	/* illegal character */
309 
310 # define NSTATES	6	/* number of states */
311 # define TYPE		017	/* mask to select state type */
312 
313 /* meta bits for table */
314 # define M		020	/* meta character; don't pass through */
315 # define B		040	/* cause a break */
316 # define MB		M|B	/* meta-break */
317 
318 static short StateTab[NSTATES][NSTATES] =
319 {
320    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
321 	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
322 	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
323 	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},
324 	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},
325 	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},
326 	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},
327 };
328 
329 /* token type table -- it gets modified with $o characters */
330 static u_char	TokTypeTab[256] =
331 {
332     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
333 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
334     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
335 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
336     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
337 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
338     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
339 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
340     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
341 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
342     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
343 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
344     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
345 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
346     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
347 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
348 
349     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
350 	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
351     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
352 	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
353     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
354 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
355     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
356 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
357     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
358 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
359     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
360 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
361     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
362 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
363     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
364 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
365 };
366 
367 /* token type table for MIME parsing */
368 u_char	MimeTokenTab[256] =
369 {
370     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
371 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
372     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
373 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
374     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
375 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
376     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
377 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
378     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
379 	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
380     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
381 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
382     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
383 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
384     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
385 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
386 
387     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
388 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
389     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
390 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
391     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
392 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
393     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
394 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
395     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
396 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
397     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
398 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
399     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
400 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
401     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
402 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
403 };
404 
405 
406 # define NOCHAR		-1	/* signal nothing in lookahead token */
407 
408 char **
409 prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
410 	char *addr;
411 	int delim;
412 	char pvpbuf[];
413 	int pvpbsize;
414 	char **delimptr;
415 	u_char *toktab;
416 {
417 	register char *p;
418 	register char *q;
419 	register int c;
420 	char **avp;
421 	bool bslashmode;
422 	bool route_syntax;
423 	int cmntcnt;
424 	int anglecnt;
425 	char *tok;
426 	int state;
427 	int newstate;
428 	char *saveto = CurEnv->e_to;
429 	static char *av[MAXATOM+1];
430 	static char firsttime = TRUE;
431 	extern int errno;
432 
433 	if (firsttime)
434 	{
435 		/* initialize the token type table */
436 		char obuf[50];
437 
438 		firsttime = FALSE;
439 		if (OperatorChars == NULL)
440 		{
441 			if (ConfigLevel < 7)
442 				OperatorChars = macvalue('o', CurEnv);
443 			if (OperatorChars == NULL)
444 				OperatorChars = ".:@[]";
445 		}
446 		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
447 		strcat(obuf, DELIMCHARS);
448 		for (p = obuf; *p != '\0'; p++)
449 		{
450 			if (TokTypeTab[*p & 0xff] == ATM)
451 				TokTypeTab[*p & 0xff] = OPR;
452 		}
453 	}
454 	if (toktab == NULL)
455 		toktab = TokTypeTab;
456 
457 	/* make sure error messages don't have garbage on them */
458 	errno = 0;
459 
460 	q = pvpbuf;
461 	bslashmode = FALSE;
462 	route_syntax = FALSE;
463 	cmntcnt = 0;
464 	anglecnt = 0;
465 	avp = av;
466 	state = ATM;
467 	c = NOCHAR;
468 	p = addr;
469 	CurEnv->e_to = p;
470 	if (tTd(22, 11))
471 	{
472 		printf("prescan: ");
473 		xputs(p);
474 		(void) putchar('\n');
475 	}
476 
477 	do
478 	{
479 		/* read a token */
480 		tok = q;
481 		for (;;)
482 		{
483 			/* store away any old lookahead character */
484 			if (c != NOCHAR && !bslashmode)
485 			{
486 				/* see if there is room */
487 				if (q >= &pvpbuf[pvpbsize - 5])
488 				{
489 					usrerr("553 Address too long");
490 					if (strlen(addr) > (SIZE_T) MAXNAME)
491 						addr[MAXNAME] = '\0';
492 	returnnull:
493 					if (delimptr != NULL)
494 						*delimptr = p;
495 					CurEnv->e_to = saveto;
496 					return (NULL);
497 				}
498 
499 				/* squirrel it away */
500 				*q++ = c;
501 			}
502 
503 			/* read a new input character */
504 			c = *p++;
505 			if (c == '\0')
506 			{
507 				/* diagnose and patch up bad syntax */
508 				if (state == QST)
509 				{
510 					usrerr("653 Unbalanced '\"'");
511 					c = '"';
512 				}
513 				else if (cmntcnt > 0)
514 				{
515 					usrerr("653 Unbalanced '('");
516 					c = ')';
517 				}
518 				else if (anglecnt > 0)
519 				{
520 					c = '>';
521 					usrerr("653 Unbalanced '<'");
522 				}
523 				else
524 					break;
525 
526 				p--;
527 			}
528 			else if (c == delim && cmntcnt <= 0 && state != QST)
529 			{
530 				if (anglecnt <= 0)
531 					break;
532 
533 				/* special case for better error management */
534 				if (delim == ',' && !route_syntax)
535 				{
536 					usrerr("653 Unbalanced '<'");
537 					c = '>';
538 					p--;
539 				}
540 			}
541 
542 			if (tTd(22, 101))
543 				printf("c=%c, s=%d; ", c, state);
544 
545 			/* chew up special characters */
546 			*q = '\0';
547 			if (bslashmode)
548 			{
549 				bslashmode = FALSE;
550 
551 				/* kludge \! for naive users */
552 				if (cmntcnt > 0)
553 				{
554 					c = NOCHAR;
555 					continue;
556 				}
557 				else if (c != '!' || state == QST)
558 				{
559 					*q++ = '\\';
560 					continue;
561 				}
562 			}
563 
564 			if (c == '\\')
565 			{
566 				bslashmode = TRUE;
567 			}
568 			else if (state == QST)
569 			{
570 				/* do nothing, just avoid next clauses */
571 			}
572 			else if (c == '(')
573 			{
574 				cmntcnt++;
575 				c = NOCHAR;
576 			}
577 			else if (c == ')')
578 			{
579 				if (cmntcnt <= 0)
580 				{
581 					usrerr("653 Unbalanced ')'");
582 					c = NOCHAR;
583 				}
584 				else
585 					cmntcnt--;
586 			}
587 			else if (cmntcnt > 0)
588 				c = NOCHAR;
589 			else if (c == '<')
590 			{
591 				char *q = p;
592 
593 				anglecnt++;
594 				while (isascii(*q) && isspace(*q))
595 					q++;
596 				if (*q == '@')
597 					route_syntax = TRUE;
598 			}
599 			else if (c == '>')
600 			{
601 				if (anglecnt <= 0)
602 				{
603 					usrerr("653 Unbalanced '>'");
604 					c = NOCHAR;
605 				}
606 				else
607 					anglecnt--;
608 				route_syntax = FALSE;
609 			}
610 			else if (delim == ' ' && isascii(c) && isspace(c))
611 				c = ' ';
612 
613 			if (c == NOCHAR)
614 				continue;
615 
616 			/* see if this is end of input */
617 			if (c == delim && anglecnt <= 0 && state != QST)
618 				break;
619 
620 			newstate = StateTab[state][toktab[c & 0xff]];
621 			if (tTd(22, 101))
622 				printf("ns=%02o\n", newstate);
623 			state = newstate & TYPE;
624 			if (state == ILL)
625 			{
626 				if (isascii(c) && isprint(c))
627 					usrerr("653 Illegal character %c", c);
628 				else
629 					usrerr("653 Illegal character 0x%02x", c);
630 			}
631 			if (bitset(M, newstate))
632 				c = NOCHAR;
633 			if (bitset(B, newstate))
634 				break;
635 		}
636 
637 		/* new token */
638 		if (tok != q)
639 		{
640 			*q++ = '\0';
641 			if (tTd(22, 36))
642 			{
643 				printf("tok=");
644 				xputs(tok);
645 				(void) putchar('\n');
646 			}
647 			if (avp >= &av[MAXATOM])
648 			{
649 				usrerr("553 prescan: too many tokens");
650 				goto returnnull;
651 			}
652 			if (q - tok > MAXNAME)
653 			{
654 				usrerr("553 prescan: token too long");
655 				goto returnnull;
656 			}
657 			*avp++ = tok;
658 		}
659 	} while (c != '\0' && (c != delim || anglecnt > 0));
660 	*avp = NULL;
661 	p--;
662 	if (delimptr != NULL)
663 		*delimptr = p;
664 	if (tTd(22, 12))
665 	{
666 		printf("prescan==>");
667 		printav(av);
668 	}
669 	CurEnv->e_to = saveto;
670 	if (av[0] == NULL)
671 	{
672 		if (tTd(22, 1))
673 			printf("prescan: null leading token\n");
674 		return (NULL);
675 	}
676 	return (av);
677 }
678 /*
679 **  REWRITE -- apply rewrite rules to token vector.
680 **
681 **	This routine is an ordered production system.  Each rewrite
682 **	rule has a LHS (called the pattern) and a RHS (called the
683 **	rewrite); 'rwr' points the the current rewrite rule.
684 **
685 **	For each rewrite rule, 'avp' points the address vector we
686 **	are trying to match against, and 'pvp' points to the pattern.
687 **	If pvp points to a special match value (MATCHZANY, MATCHANY,
688 **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
689 **	matched is saved away in the match vector (pointed to by 'mvp').
690 **
691 **	When a match between avp & pvp does not match, we try to
692 **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
693 **	we must also back out the match in mvp.  If we reach a
694 **	MATCHANY or MATCHZANY we just extend the match and start
695 **	over again.
696 **
697 **	When we finally match, we rewrite the address vector
698 **	and try over again.
699 **
700 **	Parameters:
701 **		pvp -- pointer to token vector.
702 **		ruleset -- the ruleset to use for rewriting.
703 **		reclevel -- recursion level (to catch loops).
704 **		e -- the current envelope.
705 **
706 **	Returns:
707 **		A status code.  If EX_TEMPFAIL, higher level code should
708 **			attempt recovery.
709 **
710 **	Side Effects:
711 **		pvp is modified.
712 */
713 
714 struct match
715 {
716 	char	**first;	/* first token matched */
717 	char	**last;		/* last token matched */
718 	char	**pattern;	/* pointer to pattern */
719 };
720 
721 # define MAXMATCH	9	/* max params per rewrite */
722 
723 
724 int
725 rewrite(pvp, ruleset, reclevel, e)
726 	char **pvp;
727 	int ruleset;
728 	int reclevel;
729 	register ENVELOPE *e;
730 {
731 	register char *ap;		/* address pointer */
732 	register char *rp;		/* rewrite pointer */
733 	register char **avp;		/* address vector pointer */
734 	register char **rvp;		/* rewrite vector pointer */
735 	register struct match *mlp;	/* cur ptr into mlist */
736 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
737 	int ruleno;			/* current rule number */
738 	int rstat = EX_OK;		/* return status */
739 	int loopcount;
740 	struct match mlist[MAXMATCH];	/* stores match on LHS */
741 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
742 	char buf[MAXLINE];
743 	extern int callsubr __P((char**, int, ENVELOPE *));
744 	extern int sm_strcasecmp __P((char *, char *));
745 
746 	if (OpMode == MD_TEST || tTd(21, 1))
747 	{
748 		printf("rewrite: ruleset %3d   input:", ruleset);
749 		printav(pvp);
750 	}
751 	if (ruleset < 0 || ruleset >= MAXRWSETS)
752 	{
753 		syserr("554 rewrite: illegal ruleset number %d", ruleset);
754 		return EX_CONFIG;
755 	}
756 	if (reclevel++ > MaxRuleRecursion)
757 	{
758 		syserr("rewrite: excessive recursion (max %d), ruleset %d",
759 			MaxRuleRecursion, ruleset);
760 		return EX_CONFIG;
761 	}
762 	if (pvp == NULL)
763 		return EX_USAGE;
764 
765 	/*
766 	**  Run through the list of rewrite rules, applying
767 	**	any that match.
768 	*/
769 
770 	ruleno = 1;
771 	loopcount = 0;
772 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
773 	{
774 		int stat;
775 
776 		/* if already canonical, quit now */
777 		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
778 			break;
779 
780 		if (tTd(21, 12))
781 		{
782 			printf("-----trying rule:");
783 			printav(rwr->r_lhs);
784 		}
785 
786 		/* try to match on this rule */
787 		mlp = mlist;
788 		rvp = rwr->r_lhs;
789 		avp = pvp;
790 		if (++loopcount > 100)
791 		{
792 			syserr("554 Infinite loop in ruleset %d, rule %d",
793 				ruleset, ruleno);
794 			if (tTd(21, 1))
795 			{
796 				printf("workspace: ");
797 				printav(pvp);
798 			}
799 			break;
800 		}
801 
802 		while ((ap = *avp) != NULL || *rvp != NULL)
803 		{
804 			rp = *rvp;
805 			if (tTd(21, 35))
806 			{
807 				printf("ADVANCE rp=");
808 				xputs(rp);
809 				printf(", ap=");
810 				xputs(ap);
811 				printf("\n");
812 			}
813 			if (rp == NULL)
814 			{
815 				/* end-of-pattern before end-of-address */
816 				goto backup;
817 			}
818 			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
819 			    (*rp & 0377) != MATCHZERO)
820 			{
821 				/* end-of-input with patterns left */
822 				goto backup;
823 			}
824 
825 			switch (*rp & 0377)
826 			{
827 			  case MATCHCLASS:
828 				/* match any phrase in a class */
829 				mlp->pattern = rvp;
830 				mlp->first = avp;
831 	extendclass:
832 				ap = *avp;
833 				if (ap == NULL)
834 					goto backup;
835 				mlp->last = avp++;
836 				cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
837 				if (!wordinclass(buf, rp[1]))
838 				{
839 					if (tTd(21, 36))
840 					{
841 						printf("EXTEND  rp=");
842 						xputs(rp);
843 						printf(", ap=");
844 						xputs(ap);
845 						printf("\n");
846 					}
847 					goto extendclass;
848 				}
849 				if (tTd(21, 36))
850 					printf("CLMATCH\n");
851 				mlp++;
852 				break;
853 
854 			  case MATCHNCLASS:
855 				/* match any token not in a class */
856 				if (wordinclass(ap, rp[1]))
857 					goto backup;
858 
859 				/* fall through */
860 
861 			  case MATCHONE:
862 			  case MATCHANY:
863 				/* match exactly one token */
864 				mlp->pattern = rvp;
865 				mlp->first = avp;
866 				mlp->last = avp++;
867 				mlp++;
868 				break;
869 
870 			  case MATCHZANY:
871 				/* match zero or more tokens */
872 				mlp->pattern = rvp;
873 				mlp->first = avp;
874 				mlp->last = avp - 1;
875 				mlp++;
876 				break;
877 
878 			  case MATCHZERO:
879 				/* match zero tokens */
880 				break;
881 
882 			  case MACRODEXPAND:
883 				/*
884 				**  Match against run-time macro.
885 				**  This algorithm is broken for the
886 				**  general case (no recursive macros,
887 				**  improper tokenization) but should
888 				**  work for the usual cases.
889 				*/
890 
891 				ap = macvalue(rp[1], e);
892 				mlp->first = avp;
893 				if (tTd(21, 2))
894 					printf("rewrite: LHS $&%s => \"%s\"\n",
895 						macname(rp[1]),
896 						ap == NULL ? "(NULL)" : ap);
897 
898 				if (ap == NULL)
899 					break;
900 				while (*ap != '\0')
901 				{
902 					if (*avp == NULL ||
903 					    strncasecmp(ap, *avp, strlen(*avp)) != 0)
904 					{
905 						/* no match */
906 						avp = mlp->first;
907 						goto backup;
908 					}
909 					ap += strlen(*avp++);
910 				}
911 
912 				/* match */
913 				break;
914 
915 			  default:
916 				/* must have exact match */
917 				if (sm_strcasecmp(rp, ap))
918 					goto backup;
919 				avp++;
920 				break;
921 			}
922 
923 			/* successful match on this token */
924 			rvp++;
925 			continue;
926 
927 	  backup:
928 			/* match failed -- back up */
929 			while (--mlp >= mlist)
930 			{
931 				rvp = mlp->pattern;
932 				rp = *rvp;
933 				avp = mlp->last + 1;
934 				ap = *avp;
935 
936 				if (tTd(21, 36))
937 				{
938 					printf("BACKUP  rp=");
939 					xputs(rp);
940 					printf(", ap=");
941 					xputs(ap);
942 					printf("\n");
943 				}
944 
945 				if (ap == NULL)
946 				{
947 					/* run off the end -- back up again */
948 					continue;
949 				}
950 				if ((*rp & 0377) == MATCHANY ||
951 				    (*rp & 0377) == MATCHZANY)
952 				{
953 					/* extend binding and continue */
954 					mlp->last = avp++;
955 					rvp++;
956 					mlp++;
957 					break;
958 				}
959 				if ((*rp & 0377) == MATCHCLASS)
960 				{
961 					/* extend binding and try again */
962 					mlp->last = avp;
963 					goto extendclass;
964 				}
965 			}
966 
967 			if (mlp < mlist)
968 			{
969 				/* total failure to match */
970 				break;
971 			}
972 		}
973 
974 		/*
975 		**  See if we successfully matched
976 		*/
977 
978 		if (mlp < mlist || *rvp != NULL)
979 		{
980 			if (tTd(21, 10))
981 				printf("----- rule fails\n");
982 			rwr = rwr->r_next;
983 			ruleno++;
984 			loopcount = 0;
985 			continue;
986 		}
987 
988 		rvp = rwr->r_rhs;
989 		if (tTd(21, 12))
990 		{
991 			printf("-----rule matches:");
992 			printav(rvp);
993 		}
994 
995 		rp = *rvp;
996 		if ((*rp & 0377) == CANONUSER)
997 		{
998 			rvp++;
999 			rwr = rwr->r_next;
1000 			ruleno++;
1001 			loopcount = 0;
1002 		}
1003 		else if ((*rp & 0377) == CANONHOST)
1004 		{
1005 			rvp++;
1006 			rwr = NULL;
1007 		}
1008 
1009 		/* substitute */
1010 		for (avp = npvp; *rvp != NULL; rvp++)
1011 		{
1012 			register struct match *m;
1013 			register char **pp;
1014 
1015 			rp = *rvp;
1016 			if ((*rp & 0377) == MATCHREPL)
1017 			{
1018 				/* substitute from LHS */
1019 				m = &mlist[rp[1] - '1'];
1020 				if (m < mlist || m >= mlp)
1021 				{
1022 					syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
1023 						ruleset, rp[1]);
1024 					return EX_CONFIG;
1025 				}
1026 				if (tTd(21, 15))
1027 				{
1028 					printf("$%c:", rp[1]);
1029 					pp = m->first;
1030 					while (pp <= m->last)
1031 					{
1032 						printf(" %lx=\"", (u_long) *pp);
1033 						(void) fflush(stdout);
1034 						printf("%s\"", *pp++);
1035 					}
1036 					printf("\n");
1037 				}
1038 				pp = m->first;
1039 				while (pp <= m->last)
1040 				{
1041 					if (avp >= &npvp[MAXATOM])
1042 					{
1043 						syserr("554 rewrite: expansion too long");
1044 						return EX_DATAERR;
1045 					}
1046 					*avp++ = *pp++;
1047 				}
1048 			}
1049 			else
1050 			{
1051 				/* some sort of replacement */
1052 				if (avp >= &npvp[MAXATOM])
1053 				{
1054 	toolong:
1055 					syserr("554 rewrite: expansion too long");
1056 					return EX_DATAERR;
1057 				}
1058 				if ((*rp & 0377) != MACRODEXPAND)
1059 				{
1060 					/* vanilla replacement */
1061 					*avp++ = rp;
1062 				}
1063 				else
1064 				{
1065 					/* $&x replacement */
1066 					char *mval = macvalue(rp[1], e);
1067 					char **xpvp;
1068 					int trsize = 0;
1069 					static size_t pvpb1_size = 0;
1070 					static char **pvpb1 = NULL;
1071 					char pvpbuf[PSBUFSIZE];
1072 
1073 					if (tTd(21, 2))
1074 						printf("rewrite: RHS $&%s => \"%s\"\n",
1075 							macname(rp[1]),
1076 							mval == NULL ? "(NULL)" : mval);
1077 					if (mval == NULL || *mval == '\0')
1078 						continue;
1079 
1080 					/* save the remainder of the input */
1081 					for (xpvp = pvp; *xpvp != NULL; xpvp++)
1082 						trsize += sizeof *xpvp;
1083 					if (trsize > pvpb1_size)
1084 					{
1085 						if (pvpb1 != NULL)
1086 							free(pvpb1);
1087 						pvpb1 = (char **)xalloc(trsize);
1088 						pvpb1_size = trsize;
1089 					}
1090 
1091 					bcopy((char *) pvp, (char *) pvpb1, trsize);
1092 
1093 					/* scan the new replacement */
1094 					xpvp = prescan(mval, '\0', pvpbuf,
1095 						       sizeof pvpbuf, NULL, NULL);
1096 					if (xpvp == NULL)
1097 					{
1098 						/* prescan pre-printed error */
1099 						return EX_DATAERR;
1100 					}
1101 
1102 					/* insert it into the output stream */
1103 					while (*xpvp != NULL)
1104 					{
1105 						if (tTd(21, 19))
1106 							printf(" ... %s\n", *xpvp);
1107 						*avp++ = newstr(*xpvp);
1108 						if (avp >= &npvp[MAXATOM])
1109 							goto toolong;
1110 						xpvp++;
1111 					}
1112 					if (tTd(21, 19))
1113 						printf(" ... DONE\n");
1114 
1115 					/* restore the old trailing input */
1116 					bcopy((char *) pvpb1, (char *) pvp, trsize);
1117 				}
1118 			}
1119 		}
1120 		*avp++ = NULL;
1121 
1122 		/*
1123 		**  Check for any hostname/keyword lookups.
1124 		*/
1125 
1126 		for (rvp = npvp; *rvp != NULL; rvp++)
1127 		{
1128 			char **hbrvp;
1129 			char **xpvp;
1130 			int trsize;
1131 			char *replac;
1132 			int endtoken;
1133 			STAB *map;
1134 			char *mapname;
1135 			char **key_rvp;
1136 			char **arg_rvp;
1137 			char **default_rvp;
1138 			char buf[MAXNAME + 1];
1139 			char *pvpb1[MAXATOM + 1];
1140 			char *argvect[10];
1141 			char pvpbuf[PSBUFSIZE];
1142 			char *nullpvp[1];
1143 			extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
1144 
1145 			if ((**rvp & 0377) != HOSTBEGIN &&
1146 			    (**rvp & 0377) != LOOKUPBEGIN)
1147 				continue;
1148 
1149 			/*
1150 			**  Got a hostname/keyword lookup.
1151 			**
1152 			**	This could be optimized fairly easily.
1153 			*/
1154 
1155 			hbrvp = rvp;
1156 			if ((**rvp & 0377) == HOSTBEGIN)
1157 			{
1158 				endtoken = HOSTEND;
1159 				mapname = "host";
1160 			}
1161 			else
1162 			{
1163 				endtoken = LOOKUPEND;
1164 				mapname = *++rvp;
1165 			}
1166 			map = stab(mapname, ST_MAP, ST_FIND);
1167 			if (map == NULL)
1168 				syserr("554 rewrite: map %s not found", mapname);
1169 
1170 			/* extract the match part */
1171 			key_rvp = ++rvp;
1172 			default_rvp = NULL;
1173 			arg_rvp = argvect;
1174 			xpvp = NULL;
1175 			replac = pvpbuf;
1176 			while (*rvp != NULL && (**rvp & 0377) != endtoken)
1177 			{
1178 				int nodetype = **rvp & 0377;
1179 
1180 				if (nodetype != CANONHOST && nodetype != CANONUSER)
1181 				{
1182 					rvp++;
1183 					continue;
1184 				}
1185 
1186 				*rvp++ = NULL;
1187 
1188 				if (xpvp != NULL)
1189 				{
1190 					cataddr(xpvp, NULL, replac,
1191 						&pvpbuf[sizeof pvpbuf] - replac,
1192 						'\0');
1193 					*++arg_rvp = replac;
1194 					replac += strlen(replac) + 1;
1195 					xpvp = NULL;
1196 				}
1197 				switch (nodetype)
1198 				{
1199 				  case CANONHOST:
1200 					xpvp = rvp;
1201 					break;
1202 
1203 				  case CANONUSER:
1204 					default_rvp = rvp;
1205 					break;
1206 				}
1207 			}
1208 			if (*rvp != NULL)
1209 				*rvp++ = NULL;
1210 			if (xpvp != NULL)
1211 			{
1212 				cataddr(xpvp, NULL, replac,
1213 					&pvpbuf[sizeof pvpbuf] - replac,
1214 					'\0');
1215 				*++arg_rvp = replac;
1216 			}
1217 			*++arg_rvp = NULL;
1218 
1219 			/* save the remainder of the input string */
1220 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1221 			bcopy((char *) rvp, (char *) pvpb1, trsize);
1222 
1223 			/* look it up */
1224 			cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
1225 			argvect[0] = buf;
1226 			replac = map_lookup(map, buf, argvect, &rstat, e);
1227 
1228 			/* if no replacement, use default */
1229 			if (replac == NULL && default_rvp != NULL)
1230 			{
1231 				/* create the default */
1232 				cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
1233 				replac = buf;
1234 			}
1235 
1236 			if (replac == NULL)
1237 			{
1238 				xpvp = key_rvp;
1239 			}
1240 			else if (*replac == '\0')
1241 			{
1242 				/* null replacement */
1243 				nullpvp[0] = NULL;
1244 				xpvp = nullpvp;
1245 			}
1246 			else
1247 			{
1248 				/* scan the new replacement */
1249 				xpvp = prescan(replac, '\0', pvpbuf,
1250 					       sizeof pvpbuf, NULL, NULL);
1251 				if (xpvp == NULL)
1252 				{
1253 					/* prescan already printed error */
1254 					return EX_DATAERR;
1255 				}
1256 			}
1257 
1258 			/* append it to the token list */
1259 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
1260 			{
1261 				*avp++ = newstr(*xpvp);
1262 				if (avp >= &npvp[MAXATOM])
1263 					goto toolong;
1264 			}
1265 
1266 			/* restore the old trailing information */
1267 			rvp = avp - 1;
1268 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1269 				if (avp >= &npvp[MAXATOM])
1270 					goto toolong;
1271 		}
1272 
1273 		/*
1274 		**  Check for subroutine calls.
1275 		*/
1276 
1277 		stat = callsubr(npvp, reclevel, e);
1278 		if (rstat == EX_OK || stat == EX_TEMPFAIL)
1279 			rstat = stat;
1280 
1281 		/* copy vector back into original space. */
1282 		for (avp = npvp; *avp++ != NULL;)
1283 			continue;
1284 		bcopy((char *) npvp, (char *) pvp,
1285 		      (int) (avp - npvp) * sizeof *avp);
1286 
1287 		if (tTd(21, 4))
1288 		{
1289 			printf("rewritten as:");
1290 			printav(pvp);
1291 		}
1292 	}
1293 
1294 	if (OpMode == MD_TEST || tTd(21, 1))
1295 	{
1296 		printf("rewrite: ruleset %3d returns:", ruleset);
1297 		printav(pvp);
1298 	}
1299 
1300 	return rstat;
1301 }
1302 /*
1303 **  CALLSUBR -- call subroutines in rewrite vector
1304 **
1305 **	Parameters:
1306 **		pvp -- pointer to token vector.
1307 **		reclevel -- the current recursion level.
1308 **		e -- the current envelope.
1309 **
1310 **	Returns:
1311 **		The status from the subroutine call.
1312 **
1313 **	Side Effects:
1314 **		pvp is modified.
1315 */
1316 
1317 int
1318 callsubr(pvp, reclevel, e)
1319 	char **pvp;
1320 	int reclevel;
1321 	ENVELOPE *e;
1322 {
1323     	char **avp;
1324     	char **rvp;
1325 	register int i;
1326 	int subr;
1327 	int stat;
1328 	int rstat = EX_OK;
1329 	char *tpvp[MAXATOM + 1];
1330 
1331 	for (avp = pvp; *avp != NULL; avp++)
1332 	{
1333 		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
1334 		{
1335 			stripquotes(avp[1]);
1336 			subr = strtorwset(avp[1], NULL, ST_FIND);
1337 			if (subr < 0)
1338 			{
1339 				syserr("Unknown ruleset %s", avp[1]);
1340 				return EX_CONFIG;
1341 			}
1342 
1343 			if (tTd(21, 3))
1344 				printf("-----callsubr %s (%d)\n", avp[1], subr);
1345 
1346 			/*
1347 			**  Take care of possible inner calls first.
1348 			**  use a full size temporary buffer to avoid
1349 			**  overflows in rewrite, but strip off the
1350 			**  subroutine call.
1351 			*/
1352 
1353 			for (i = 2; avp[i] != NULL; i++)
1354 				tpvp[i - 2] = avp[i];
1355 			tpvp[i - 2] = NULL;
1356 
1357 			stat = callsubr(tpvp, reclevel, e);
1358 			if (rstat == EX_OK || stat == EX_TEMPFAIL)
1359 				rstat = stat;
1360 
1361 			/*
1362 			**  Now we need to call the ruleset specified for
1363 			**  the subroutine. we can do this with the
1364 			**  temporary buffer that we set up earlier,
1365 			**  since it has all the data we want to rewrite.
1366 			*/
1367 
1368 			stat = rewrite(tpvp, subr, reclevel, e);
1369 			if (rstat == EX_OK || stat == EX_TEMPFAIL)
1370 				rstat = stat;
1371 
1372 			/*
1373 			**  Find length of tpvp and current offset into
1374 			**  pvp, if the total is greater than MAXATOM,
1375 			**  then it would overflow the buffer if we copied
1376 			**  it back in to pvp, in which case we throw a
1377 			**  fit.
1378 			*/
1379 
1380 			for (rvp = tpvp; *rvp != NULL; rvp++)
1381 				continue;
1382 			if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
1383 			{
1384 			    	syserr("554 callsubr: expansion too long");
1385 				return EX_DATAERR;
1386 			}
1387 
1388 			/*
1389 			**  Now we can copy the rewritten code over
1390 			**  the initial subroutine call in the buffer.
1391 			*/
1392 
1393 			for (i = 0; tpvp[i] != NULL; i++)
1394 				avp[i] = tpvp[i];
1395 			avp[i] = NULL;
1396 
1397 			/*
1398 			**  If we got this far, we've processed the left
1399 			**  most subroutine, and recursively called ourselves
1400 			**  to handle any other subroutines.  We're done.
1401 			*/
1402 
1403 			break;
1404 		}
1405 	}
1406 	return rstat;
1407 }
1408 /*
1409 **  MAP_LOOKUP -- do lookup in map
1410 **
1411 **	Parameters:
1412 **		map -- the map to use for the lookup.
1413 **		key -- the key to look up.
1414 **		argvect -- arguments to pass to the map lookup.
1415 **		pstat -- a pointer to an integer in which to store the
1416 **			status from the lookup.
1417 **		e -- the current envelope.
1418 **
1419 **	Returns:
1420 **		The result of the lookup.
1421 **		NULL -- if there was no data for the given key.
1422 */
1423 
1424 char *
1425 map_lookup(map, key, argvect, pstat, e)
1426 	STAB *map;
1427 	char key[];
1428 	char **argvect;
1429 	int *pstat;
1430 	ENVELOPE *e;
1431 {
1432 	auto int stat = EX_OK;
1433 	char *replac;
1434 
1435 	if (e->e_sendmode == SM_DEFER)
1436 	{
1437 		/* don't do any map lookups */
1438 		if (tTd(60, 1))
1439 			printf("map_lookup(%s, %s) => DEFERRED\n",
1440 				map->s_name, key);
1441 		*pstat = EX_TEMPFAIL;
1442 		return NULL;
1443 	}
1444 	if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags))
1445 		return NULL;
1446 
1447 	if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
1448 		stripquotes(key);
1449 
1450 	/* XXX should try to auto-open the map here */
1451 
1452 	if (tTd(60, 1))
1453 		printf("map_lookup(%s, %s) => ",
1454 			map->s_name, key);
1455 	replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
1456 			key, argvect, &stat);
1457 	if (tTd(60, 1))
1458 		printf("%s (%d)\n",
1459 			replac != NULL ? replac : "NOT FOUND",
1460 			stat);
1461 
1462 	/* should recover if stat == EX_TEMPFAIL */
1463 	if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags))
1464 	{
1465 		*pstat = EX_TEMPFAIL;
1466 		if (tTd(60, 1))
1467 			printf("map_lookup(%s, %s) tempfail: errno=%d\n",
1468 				map->s_name, key, errno);
1469 		if (e->e_message == NULL)
1470 		{
1471 			char mbuf[320];
1472 
1473 			snprintf(mbuf, sizeof mbuf,
1474 				"%.80s map: lookup (%s): deferred",
1475 				map->s_name,
1476 				shortenstring(key, MAXSHORTSTR));
1477 			e->e_message = newstr(mbuf);
1478 		}
1479 	}
1480 	if (stat == EX_TEMPFAIL && map->s_map.map_tapp != NULL)
1481 	{
1482 		size_t i = strlen(key) + strlen(map->s_map.map_tapp) + 1;
1483 		static char *rwbuf = NULL;
1484 		static size_t rwbuflen = 0;
1485 
1486 		if (i > rwbuflen)
1487 		{
1488 			if (rwbuf != NULL)
1489 				free(rwbuf);
1490 			rwbuflen = i;
1491 			rwbuf = (char *) xalloc(rwbuflen);
1492 		}
1493 		snprintf(rwbuf, rwbuflen, "%s%s", key, map->s_map.map_tapp);
1494 		if (tTd(60, 4))
1495 			printf("map_lookup tempfail: returning \"%s\"\n",
1496 				rwbuf);
1497 		return rwbuf;
1498 	}
1499 	return replac;
1500 }
1501 /*
1502 **  BUILDADDR -- build address from token vector.
1503 **
1504 **	Parameters:
1505 **		tv -- token vector.
1506 **		a -- pointer to address descriptor to fill.
1507 **			If NULL, one will be allocated.
1508 **		flags -- info regarding whether this is a sender or
1509 **			a recipient.
1510 **		e -- the current envelope.
1511 **
1512 **	Returns:
1513 **		NULL if there was an error.
1514 **		'a' otherwise.
1515 **
1516 **	Side Effects:
1517 **		fills in 'a'
1518 */
1519 
1520 struct errcodes
1521 {
1522 	char	*ec_name;		/* name of error code */
1523 	int	ec_code;		/* numeric code */
1524 } ErrorCodes[] =
1525 {
1526 	{ "usage",		EX_USAGE	},
1527 	{ "nouser",		EX_NOUSER	},
1528 	{ "nohost",		EX_NOHOST	},
1529 	{ "unavailable",	EX_UNAVAILABLE	},
1530 	{ "software",		EX_SOFTWARE	},
1531 	{ "tempfail",		EX_TEMPFAIL	},
1532 	{ "protocol",		EX_PROTOCOL	},
1533 #ifdef EX_CONFIG
1534 	{ "config",		EX_CONFIG	},
1535 #endif
1536 	{ NULL,			EX_UNAVAILABLE	}
1537 };
1538 
1539 ADDRESS *
1540 buildaddr(tv, a, flags, e)
1541 	register char **tv;
1542 	register ADDRESS *a;
1543 	int flags;
1544 	register ENVELOPE *e;
1545 {
1546 	struct mailer **mp;
1547 	register struct mailer *m;
1548 	register char *p;
1549 	char *mname;
1550 	char **hostp;
1551 	char hbuf[MAXNAME + 1];
1552 	static MAILER discardmailer;
1553 	static MAILER errormailer;
1554 	static char *discardargv[] = { "DISCARD", NULL };
1555 	static char *errorargv[] = { "ERROR", NULL };
1556 	static char ubuf[MAXNAME + 1];
1557 
1558 	if (tTd(24, 5))
1559 	{
1560 		printf("buildaddr, flags=%x, tv=", flags);
1561 		printav(tv);
1562 	}
1563 
1564 	if (a == NULL)
1565 		a = (ADDRESS *) xalloc(sizeof *a);
1566 	bzero((char *) a, sizeof *a);
1567 
1568 	/* set up default error return flags */
1569 	a->q_flags |= DefaultNotify;
1570 
1571 	if (discardmailer.m_name == NULL)
1572 	{
1573 		/* initialize the discard mailer */
1574 		discardmailer.m_name = "*discard*";
1575 		discardmailer.m_mailer = "DISCARD";
1576 		discardmailer.m_argv = discardargv;
1577 	}
1578 
1579 	/* figure out what net/mailer to use */
1580 	if (*tv == NULL || (**tv & 0377) != CANONNET)
1581 	{
1582 		syserr("554 buildaddr: no mailer in parsed address");
1583 badaddr:
1584 		a->q_flags |= QBADADDR;
1585 		a->q_mailer = &errormailer;
1586 		if (errormailer.m_name == NULL)
1587 		{
1588 			/* initialize the bogus mailer */
1589 			errormailer.m_name = "*error*";
1590 			errormailer.m_mailer = "ERROR";
1591 			errormailer.m_argv = errorargv;
1592 		}
1593 		return a;
1594 	}
1595 	mname = *++tv;
1596 
1597 	/* extract host and user portions */
1598 	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1599 		hostp = ++tv;
1600 	else
1601 		hostp = NULL;
1602 	while (*tv != NULL && (**tv & 0377) != CANONUSER)
1603 		tv++;
1604 	if (*tv == NULL)
1605 	{
1606 		syserr("554 buildaddr: no user");
1607 		goto badaddr;
1608 	}
1609 	if (tv == hostp)
1610 		hostp = NULL;
1611 	else if (hostp != NULL)
1612 		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1613 	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1614 
1615 	/* save away the host name */
1616 	if (strcasecmp(mname, "error") == 0)
1617 	{
1618 		if (hostp != NULL)
1619 		{
1620 			register struct errcodes *ep;
1621 
1622 			if (strchr(hbuf, '.') != NULL)
1623 			{
1624 				extern int dsntoexitstat __P((char *));
1625 
1626 				a->q_status = newstr(hbuf);
1627 				setstat(dsntoexitstat(hbuf));
1628 			}
1629 			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
1630 			{
1631 				setstat(atoi(hbuf));
1632 			}
1633 			else
1634 			{
1635 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1636 					if (strcasecmp(ep->ec_name, hbuf) == 0)
1637 						break;
1638 				setstat(ep->ec_code);
1639 			}
1640 		}
1641 		else
1642 			setstat(EX_UNAVAILABLE);
1643 		stripquotes(ubuf);
1644 		if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
1645 		    isascii(ubuf[1]) && isdigit(ubuf[1]) &&
1646 		    isascii(ubuf[2]) && isdigit(ubuf[2]) &&
1647 		    ubuf[3] == ' ')
1648 		{
1649 			char fmt[10];
1650 
1651 			strncpy(fmt, ubuf, 3);
1652 			strcpy(&fmt[3], " %s");
1653 			usrerr(fmt, ubuf + 4);
1654 
1655 			/*
1656 			**  If this is a 4xx code and we aren't running
1657 			**  SMTP on our input, bounce this message;
1658 			**  otherwise it disappears without a trace.
1659 			*/
1660 
1661 			if (fmt[0] == '4' && OpMode != MD_SMTP &&
1662 			    OpMode != MD_DAEMON)
1663 			{
1664 				e->e_flags |= EF_FATALERRS;
1665 			}
1666 		}
1667 		else
1668 		{
1669 			usrerr("553 %s", ubuf);
1670 		}
1671 		goto badaddr;
1672 	}
1673 
1674 	for (mp = Mailer; (m = *mp++) != NULL; )
1675 	{
1676 		if (strcasecmp(m->m_name, mname) == 0)
1677 			break;
1678 	}
1679 	if (m == NULL)
1680 	{
1681 		syserr("554 buildaddr: unknown mailer %s", mname);
1682 		goto badaddr;
1683 	}
1684 	a->q_mailer = m;
1685 
1686 	/* figure out what host (if any) */
1687 	if (hostp == NULL)
1688 	{
1689 		if (!bitnset(M_LOCALMAILER, m->m_flags))
1690 		{
1691 			syserr("554 buildaddr: no host");
1692 			goto badaddr;
1693 		}
1694 		a->q_host = NULL;
1695 	}
1696 	else
1697 		a->q_host = newstr(hbuf);
1698 
1699 	/* figure out the user */
1700 	p = ubuf;
1701 	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
1702 	{
1703 		p++;
1704 		tv++;
1705 		a->q_flags |= QNOTREMOTE;
1706 	}
1707 
1708 	/* do special mapping for local mailer */
1709 	if (*p == '"')
1710 		p++;
1711 	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1712 		a->q_mailer = m = ProgMailer;
1713 	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1714 		a->q_mailer = m = FileMailer;
1715 	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
1716 	{
1717 		/* may be :include: */
1718 		stripquotes(ubuf);
1719 		if (strncasecmp(ubuf, ":include:", 9) == 0)
1720 		{
1721 			/* if :include:, don't need further rewriting */
1722 			a->q_mailer = m = InclMailer;
1723 			a->q_user = newstr(&ubuf[9]);
1724 			return a;
1725 		}
1726 	}
1727 
1728 	/* rewrite according recipient mailer rewriting rules */
1729 	define('h', a->q_host, e);
1730 	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1731 	{
1732 		/* sender addresses done later */
1733 		(void) rewrite(tv, 2, 0, e);
1734 		if (m->m_re_rwset > 0)
1735 		       (void) rewrite(tv, m->m_re_rwset, 0, e);
1736 	}
1737 	(void) rewrite(tv, 4, 0, e);
1738 
1739 	/* save the result for the command line/RCPT argument */
1740 	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
1741 	a->q_user = ubuf;
1742 
1743 	/*
1744 	**  Do mapping to lower case as requested by mailer
1745 	*/
1746 
1747 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1748 		makelower(a->q_host);
1749 	if (!bitnset(M_USR_UPPER, m->m_flags))
1750 		makelower(a->q_user);
1751 
1752 	if (tTd(24, 6))
1753 	{
1754 		printf("buildaddr => ");
1755 		printaddr(a, FALSE);
1756 	}
1757 	return a;
1758 }
1759 /*
1760 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1761 **
1762 **	Parameters:
1763 **		pvp -- parameter vector to rebuild.
1764 **		evp -- last parameter to include.  Can be NULL to
1765 **			use entire pvp.
1766 **		buf -- buffer to build the string into.
1767 **		sz -- size of buf.
1768 **		spacesub -- the space separator character; if null,
1769 **			use SpaceSub.
1770 **
1771 **	Returns:
1772 **		none.
1773 **
1774 **	Side Effects:
1775 **		Destroys buf.
1776 */
1777 
1778 void
1779 cataddr(pvp, evp, buf, sz, spacesub)
1780 	char **pvp;
1781 	char **evp;
1782 	char *buf;
1783 	register int sz;
1784 	int spacesub;
1785 {
1786 	bool oatomtok = FALSE;
1787 	bool natomtok = FALSE;
1788 	register int i;
1789 	register char *p;
1790 
1791 	if (spacesub == '\0')
1792 		spacesub = SpaceSub;
1793 
1794 	if (pvp == NULL)
1795 	{
1796 		(void) strcpy(buf, "");
1797 		return;
1798 	}
1799 	p = buf;
1800 	sz -= 2;
1801 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1802 	{
1803 		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
1804 		if (oatomtok && natomtok)
1805 			*p++ = spacesub;
1806 		(void) strcpy(p, *pvp);
1807 		oatomtok = natomtok;
1808 		p += i;
1809 		sz -= i + 1;
1810 		if (pvp++ == evp)
1811 			break;
1812 	}
1813 	*p = '\0';
1814 }
1815 /*
1816 **  SAMEADDR -- Determine if two addresses are the same
1817 **
1818 **	This is not just a straight comparison -- if the mailer doesn't
1819 **	care about the host we just ignore it, etc.
1820 **
1821 **	Parameters:
1822 **		a, b -- pointers to the internal forms to compare.
1823 **
1824 **	Returns:
1825 **		TRUE -- they represent the same mailbox.
1826 **		FALSE -- they don't.
1827 **
1828 **	Side Effects:
1829 **		none.
1830 */
1831 
1832 bool
1833 sameaddr(a, b)
1834 	register ADDRESS *a;
1835 	register ADDRESS *b;
1836 {
1837 	register ADDRESS *ca, *cb;
1838 
1839 	/* if they don't have the same mailer, forget it */
1840 	if (a->q_mailer != b->q_mailer)
1841 		return (FALSE);
1842 
1843 	/* if the user isn't the same, we can drop out */
1844 	if (strcmp(a->q_user, b->q_user) != 0)
1845 		return (FALSE);
1846 
1847 	/* if we have good uids for both but they differ, these are different */
1848 	if (a->q_mailer == ProgMailer)
1849 	{
1850 		ca = getctladdr(a);
1851 		cb = getctladdr(b);
1852 		if (ca != NULL && cb != NULL &&
1853 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1854 		    ca->q_uid != cb->q_uid)
1855 			return (FALSE);
1856 	}
1857 
1858 	/* otherwise compare hosts (but be careful for NULL ptrs) */
1859 	if (a->q_host == b->q_host)
1860 	{
1861 		/* probably both null pointers */
1862 		return (TRUE);
1863 	}
1864 	if (a->q_host == NULL || b->q_host == NULL)
1865 	{
1866 		/* only one is a null pointer */
1867 		return (FALSE);
1868 	}
1869 	if (strcmp(a->q_host, b->q_host) != 0)
1870 		return (FALSE);
1871 
1872 	return (TRUE);
1873 }
1874 /*
1875 **  PRINTADDR -- print address (for debugging)
1876 **
1877 **	Parameters:
1878 **		a -- the address to print
1879 **		follow -- follow the q_next chain.
1880 **
1881 **	Returns:
1882 **		none.
1883 **
1884 **	Side Effects:
1885 **		none.
1886 */
1887 
1888 struct qflags
1889 {
1890 	char	*qf_name;
1891 	u_long	qf_bit;
1892 };
1893 
1894 struct qflags	AddressFlags[] =
1895 {
1896 	{ "QDONTSEND",		QDONTSEND	},
1897 	{ "QBADADDR",		QBADADDR	},
1898 	{ "QGOODUID",		QGOODUID	},
1899 	{ "QPRIMARY",		QPRIMARY	},
1900 	{ "QQUEUEUP",		QQUEUEUP	},
1901 	{ "QSENT",		QSENT		},
1902 	{ "QNOTREMOTE",		QNOTREMOTE	},
1903 	{ "QSELFREF",		QSELFREF	},
1904 	{ "QVERIFIED",		QVERIFIED	},
1905 	{ "QBOGUSSHELL",	QBOGUSSHELL	},
1906 	{ "QUNSAFEADDR",	QUNSAFEADDR	},
1907 	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
1908 	{ "QPINGONFAILURE",	QPINGONFAILURE	},
1909 	{ "QPINGONDELAY",	QPINGONDELAY	},
1910 	{ "QHASNOTIFY",		QHASNOTIFY	},
1911 	{ "QRELAYED",		QRELAYED	},
1912 	{ "QEXPANDED",		QEXPANDED	},
1913 	{ "QDELIVERED",		QDELIVERED	},
1914 	{ "QDELAYED",		QDELAYED	},
1915 	{ "QTHISPASS",		QTHISPASS	},
1916 	{ "QRCPTOK",		QRCPTOK		},
1917 	{ NULL }
1918 };
1919 
1920 void
1921 printaddr(a, follow)
1922 	register ADDRESS *a;
1923 	bool follow;
1924 {
1925 	register MAILER *m;
1926 	MAILER pseudomailer;
1927 	register struct qflags *qfp;
1928 	bool firstone;
1929 
1930 	if (a == NULL)
1931 	{
1932 		printf("[NULL]\n");
1933 		return;
1934 	}
1935 
1936 	while (a != NULL)
1937 	{
1938 		printf("%lx=", (u_long) a);
1939 		(void) fflush(stdout);
1940 
1941 		/* find the mailer -- carefully */
1942 		m = a->q_mailer;
1943 		if (m == NULL)
1944 		{
1945 			m = &pseudomailer;
1946 			m->m_mno = -1;
1947 			m->m_name = "NULL";
1948 		}
1949 
1950 		printf("%s:\n\tmailer %d (%s), host `%s'\n",
1951 		       a->q_paddr == NULL ? "<null>" : a->q_paddr,
1952 		       m->m_mno, m->m_name,
1953 		       a->q_host == NULL ? "<null>" : a->q_host);
1954 		printf("\tuser `%s', ruser `%s'\n",
1955 		       a->q_user,
1956 		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
1957 		printf("\tnext=%lx, alias %lx, uid %d, gid %d\n",
1958 		       (u_long) a->q_next, (u_long) a->q_alias,
1959 		       (int) a->q_uid, (int) a->q_gid);
1960 		printf("\tflags=%lx<", a->q_flags);
1961 		firstone = TRUE;
1962 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
1963 		{
1964 			if (!bitset(qfp->qf_bit, a->q_flags))
1965 				continue;
1966 			if (!firstone)
1967 				printf(",");
1968 			firstone = FALSE;
1969 			printf("%s", qfp->qf_name);
1970 		}
1971 		printf(">\n");
1972 		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
1973 		       a->q_owner == NULL ? "(none)" : a->q_owner,
1974 		       a->q_home == NULL ? "(none)" : a->q_home,
1975 		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
1976 		printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
1977 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
1978 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
1979 		       a->q_status == NULL ? "(none)" : a->q_status);
1980 		printf("\trstatus=\"%s\"\n",
1981 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
1982 		printf("\tspecificity=%d, statdate=%s\n",
1983 			a->q_specificity, ctime(&a->q_statdate));
1984 
1985 		if (!follow)
1986 			return;
1987 		a = a->q_next;
1988 	}
1989 }
1990 /*
1991 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
1992 **
1993 **	Parameters:
1994 **		a -- pointer to the address
1995 **
1996 **	Returns:
1997 **		TRUE -- if this address is "empty" (i.e., no one should
1998 **			ever generate replies to it.
1999 **		FALSE -- if it is a "regular" (read: replyable) address.
2000 */
2001 
2002 bool
2003 emptyaddr(a)
2004 	register ADDRESS *a;
2005 {
2006 	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2007 	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2008 }
2009 /*
2010 **  REMOTENAME -- return the name relative to the current mailer
2011 **
2012 **	Parameters:
2013 **		name -- the name to translate.
2014 **		m -- the mailer that we want to do rewriting relative
2015 **			to.
2016 **		flags -- fine tune operations.
2017 **		pstat -- pointer to status word.
2018 **		e -- the current envelope.
2019 **
2020 **	Returns:
2021 **		the text string representing this address relative to
2022 **			the receiving mailer.
2023 **
2024 **	Side Effects:
2025 **		none.
2026 **
2027 **	Warnings:
2028 **		The text string returned is tucked away locally;
2029 **			copy it if you intend to save it.
2030 */
2031 
2032 char *
2033 remotename(name, m, flags, pstat, e)
2034 	char *name;
2035 	struct mailer *m;
2036 	int flags;
2037 	int *pstat;
2038 	register ENVELOPE *e;
2039 {
2040 	register char **pvp;
2041 	char *fancy;
2042 	char *oldg = macvalue('g', e);
2043 	int rwset;
2044 	static char buf[MAXNAME + 1];
2045 	char lbuf[MAXNAME + 1];
2046 	char pvpbuf[PSBUFSIZE];
2047 	extern char *crackaddr __P((char *));
2048 
2049 	if (tTd(12, 1))
2050 		printf("remotename(%s)\n", name);
2051 
2052 	/* don't do anything if we are tagging it as special */
2053 	if (bitset(RF_SENDERADDR, flags))
2054 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2055 						     : m->m_se_rwset;
2056 	else
2057 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2058 						     : m->m_re_rwset;
2059 	if (rwset < 0)
2060 		return (name);
2061 
2062 	/*
2063 	**  Do a heuristic crack of this name to extract any comment info.
2064 	**	This will leave the name as a comment and a $g macro.
2065 	*/
2066 
2067 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2068 		fancy = "\201g";
2069 	else
2070 		fancy = crackaddr(name);
2071 
2072 	/*
2073 	**  Turn the name into canonical form.
2074 	**	Normally this will be RFC 822 style, i.e., "user@domain".
2075 	**	If this only resolves to "user", and the "C" flag is
2076 	**	specified in the sending mailer, then the sender's
2077 	**	domain will be appended.
2078 	*/
2079 
2080 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2081 	if (pvp == NULL)
2082 		return (name);
2083 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2084 		*pstat = EX_TEMPFAIL;
2085 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2086 	{
2087 		/* append from domain to this address */
2088 		register char **pxp = pvp;
2089 
2090 		/* see if there is an "@domain" in the current name */
2091 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2092 			pxp++;
2093 		if (*pxp == NULL)
2094 		{
2095 			/* no.... append the "@domain" from the sender */
2096 			register char **qxq = e->e_fromdomain;
2097 
2098 			while ((*pxp++ = *qxq++) != NULL)
2099 				continue;
2100 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2101 				*pstat = EX_TEMPFAIL;
2102 		}
2103 	}
2104 
2105 	/*
2106 	**  Do more specific rewriting.
2107 	**	Rewrite using ruleset 1 or 2 depending on whether this is
2108 	**		a sender address or not.
2109 	**	Then run it through any receiving-mailer-specific rulesets.
2110 	*/
2111 
2112 	if (bitset(RF_SENDERADDR, flags))
2113 	{
2114 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
2115 			*pstat = EX_TEMPFAIL;
2116 	}
2117 	else
2118 	{
2119 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
2120 			*pstat = EX_TEMPFAIL;
2121 	}
2122 	if (rwset > 0)
2123 	{
2124 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
2125 			*pstat = EX_TEMPFAIL;
2126 	}
2127 
2128 	/*
2129 	**  Do any final sanitation the address may require.
2130 	**	This will normally be used to turn internal forms
2131 	**	(e.g., user@host.LOCAL) into external form.  This
2132 	**	may be used as a default to the above rules.
2133 	*/
2134 
2135 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
2136 		*pstat = EX_TEMPFAIL;
2137 
2138 	/*
2139 	**  Now restore the comment information we had at the beginning.
2140 	*/
2141 
2142 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
2143 	define('g', lbuf, e);
2144 
2145 	/* need to make sure route-addrs have <angle brackets> */
2146 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2147 		expand("<\201g>", buf, sizeof buf, e);
2148 	else
2149 		expand(fancy, buf, sizeof buf, e);
2150 
2151 	define('g', oldg, e);
2152 
2153 	if (tTd(12, 1))
2154 		printf("remotename => `%s'\n", buf);
2155 	return (buf);
2156 }
2157 /*
2158 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2159 **
2160 **	Parameters:
2161 **		a -- the address to map (but just the user name part).
2162 **		sendq -- the sendq in which to install any replacement
2163 **			addresses.
2164 **		aliaslevel -- the alias nesting depth.
2165 **		e -- the envelope.
2166 **
2167 **	Returns:
2168 **		none.
2169 */
2170 
2171 #define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2172 			 Q_PINGFLAGS|QHASNOTIFY|\
2173 			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
2174 
2175 void
2176 maplocaluser(a, sendq, aliaslevel, e)
2177 	register ADDRESS *a;
2178 	ADDRESS **sendq;
2179 	int aliaslevel;
2180 	ENVELOPE *e;
2181 {
2182 	register char **pvp;
2183 	register ADDRESS *a1 = NULL;
2184 	auto char *delimptr;
2185 	char pvpbuf[PSBUFSIZE];
2186 
2187 	if (tTd(29, 1))
2188 	{
2189 		printf("maplocaluser: ");
2190 		printaddr(a, FALSE);
2191 	}
2192 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
2193 	if (pvp == NULL)
2194 	{
2195 		if (tTd(29, 9))
2196 			printf("maplocaluser: cannot prescan %s\n", a->q_user);
2197 		return;
2198 	}
2199 
2200 	define('h', a->q_host, e);
2201 	define('u', a->q_user, e);
2202 	define('z', a->q_home, e);
2203 
2204 	if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
2205 	{
2206 		if (tTd(29, 9))
2207 			printf("maplocaluser: rewrite tempfail\n");
2208 		a->q_flags |= QQUEUEUP;
2209 		a->q_status = "4.4.3";
2210 		return;
2211 	}
2212 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2213 	{
2214 		if (tTd(29, 9))
2215 			printf("maplocaluser: doesn't resolve\n");
2216 		return;
2217 	}
2218 
2219 	/* if non-null, mailer destination specified -- has it changed? */
2220 	a1 = buildaddr(pvp, NULL, 0, e);
2221 	if (a1 == NULL || sameaddr(a, a1))
2222 	{
2223 		if (tTd(29, 9))
2224 			printf("maplocaluser: address unchanged\n");
2225 		if (a1 != NULL)
2226 			free(a1);
2227 		return;
2228 	}
2229 
2230 	/* make new address take on flags and print attributes of old */
2231 	a1->q_flags &= ~Q_COPYFLAGS;
2232 	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2233 	a1->q_paddr = a->q_paddr;
2234 
2235 	/* mark old address as dead; insert new address */
2236 	a->q_flags |= QDONTSEND;
2237 	if (tTd(29, 5))
2238 	{
2239 		printf("maplocaluser: QDONTSEND ");
2240 		printaddr(a, FALSE);
2241 	}
2242 	a1->q_alias = a;
2243 	allocaddr(a1, RF_COPYALL, a->q_paddr);
2244 	(void) recipient(a1, sendq, aliaslevel, e);
2245 }
2246 /*
2247 **  DEQUOTE_INIT -- initialize dequote map
2248 **
2249 **	This is a no-op.
2250 **
2251 **	Parameters:
2252 **		map -- the internal map structure.
2253 **		args -- arguments.
2254 **
2255 **	Returns:
2256 **		TRUE.
2257 */
2258 
2259 bool
2260 dequote_init(map, args)
2261 	MAP *map;
2262 	char *args;
2263 {
2264 	register char *p = args;
2265 
2266 	map->map_mflags |= MF_KEEPQUOTES;
2267 	for (;;)
2268 	{
2269 		while (isascii(*p) && isspace(*p))
2270 			p++;
2271 		if (*p != '-')
2272 			break;
2273 		switch (*++p)
2274 		{
2275 		  case 'a':
2276 			map->map_app = ++p;
2277 			break;
2278 
2279 		  case 's':
2280 			map->map_coldelim = *++p;
2281 			break;
2282 		}
2283 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2284 			p++;
2285 		if (*p != '\0')
2286 			*p = '\0';
2287 	}
2288 	if (map->map_app != NULL)
2289 		map->map_app = newstr(map->map_app);
2290 
2291 	return TRUE;
2292 }
2293 /*
2294 **  DEQUOTE_MAP -- unquote an address
2295 **
2296 **	Parameters:
2297 **		map -- the internal map structure (ignored).
2298 **		name -- the name to dequote.
2299 **		av -- arguments (ignored).
2300 **		statp -- pointer to status out-parameter.
2301 **
2302 **	Returns:
2303 **		NULL -- if there were no quotes, or if the resulting
2304 **			unquoted buffer would not be acceptable to prescan.
2305 **		else -- The dequoted buffer.
2306 */
2307 
2308 /* ARGSUSED2 */
2309 char *
2310 dequote_map(map, name, av, statp)
2311 	MAP *map;
2312 	char *name;
2313 	char **av;
2314 	int *statp;
2315 {
2316 	register char *p;
2317 	register char *q;
2318 	register char c;
2319 	int anglecnt = 0;
2320 	int cmntcnt = 0;
2321 	int quotecnt = 0;
2322 	int spacecnt = 0;
2323 	bool quotemode = FALSE;
2324 	bool bslashmode = FALSE;
2325 	char spacesub = map->map_coldelim;
2326 
2327 	for (p = q = name; (c = *p++) != '\0'; )
2328 	{
2329 		if (bslashmode)
2330 		{
2331 			bslashmode = FALSE;
2332 			*q++ = c;
2333 			continue;
2334 		}
2335 
2336 		if (c == ' ' && spacesub != '\0')
2337 			c = spacesub;
2338 
2339 		switch (c)
2340 		{
2341 		  case '\\':
2342 			bslashmode = TRUE;
2343 			break;
2344 
2345 		  case '(':
2346 			cmntcnt++;
2347 			break;
2348 
2349 		  case ')':
2350 			if (cmntcnt-- <= 0)
2351 				return NULL;
2352 			break;
2353 
2354 		  case ' ':
2355 			spacecnt++;
2356 			break;
2357 		}
2358 
2359 		if (cmntcnt > 0)
2360 		{
2361 			*q++ = c;
2362 			continue;
2363 		}
2364 
2365 		switch (c)
2366 		{
2367 		  case '"':
2368 			quotemode = !quotemode;
2369 			quotecnt++;
2370 			continue;
2371 
2372 		  case '<':
2373 			anglecnt++;
2374 			break;
2375 
2376 		  case '>':
2377 			if (anglecnt-- <= 0)
2378 				return NULL;
2379 			break;
2380 		}
2381 		*q++ = c;
2382 	}
2383 
2384 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2385 	    quotemode || quotecnt <= 0 || spacecnt != 0)
2386 		return NULL;
2387 	*q++ = '\0';
2388 	return map_rewrite(map, name, strlen(name), NULL);
2389 }
2390 /*
2391 **  RSCHECK -- check string(s) for validity using rewriting sets
2392 **
2393 **	Parameters:
2394 **		rwset -- the rewriting set to use.
2395 **		p1 -- the first string to check.
2396 **		p2 -- the second string to check -- may be null.
2397 **		e -- the current envelope.
2398 **
2399 **	Returns:
2400 **		EX_OK -- if the rwset doesn't resolve to $#error
2401 **		else -- the failure status (message printed)
2402 */
2403 
2404 int
2405 rscheck(rwset, p1, p2, e)
2406 	char *rwset;
2407 	char *p1;
2408 	char *p2;
2409 	ENVELOPE *e;
2410 {
2411 	char *buf;
2412 	int bufsize;
2413 	int saveexitstat;
2414 	int rstat = EX_OK;
2415 	char **pvp;
2416 	int rsno;
2417 	bool discard = FALSE;
2418 	auto ADDRESS a1;
2419 	bool saveQuickAbort = QuickAbort;
2420 	bool saveSuprErrs = SuprErrs;
2421 	char buf0[MAXLINE];
2422 	char pvpbuf[PSBUFSIZE];
2423 	extern char MsgBuf[];
2424 
2425 	if (tTd(48, 2))
2426 		printf("rscheck(%s, %s, %s)\n", rwset, p1,
2427 			p2 == NULL ? "(NULL)" : p2);
2428 
2429 	rsno = strtorwset(rwset, NULL, ST_FIND);
2430 	if (rsno < 0)
2431 		return EX_OK;
2432 
2433 	if (p2 != NULL)
2434 	{
2435 		bufsize = strlen(p1) + strlen(p2) + 2;
2436 		if (bufsize > sizeof buf0)
2437 			buf = xalloc(bufsize);
2438 		else
2439 		{
2440 			buf = buf0;
2441 			bufsize = sizeof buf0;
2442 		}
2443 		(void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
2444 	}
2445 	else
2446 	{
2447 		bufsize = strlen(p1) + 1;
2448 		if (bufsize > sizeof buf0)
2449 			buf = xalloc(bufsize);
2450 		else
2451 		{
2452 			buf = buf0;
2453 			bufsize = sizeof buf0;
2454 		}
2455 		(void) snprintf(buf, bufsize, "%s", p1);
2456 	}
2457 	SuprErrs = TRUE;
2458 	QuickAbort = FALSE;
2459 	pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2460 	SuprErrs = saveSuprErrs;
2461 	if (pvp == NULL)
2462 	{
2463 		if (tTd(48, 2))
2464 			printf("rscheck: cannot prescan input\n");
2465 /*
2466 		syserr("rscheck: cannot prescan input: \"%s\"",
2467 			shortenstring(buf, MAXSHORTSTR));
2468 		rstat = EX_DATAERR;
2469 */
2470 		goto finis;
2471 	}
2472 	(void) rewrite(pvp, rsno, 0, e);
2473 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
2474 	    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
2475 			       strcmp(pvp[1], "discard") != 0))
2476 	{
2477 		goto finis;
2478 	}
2479 
2480 	if (strcmp(pvp[1], "discard") == 0)
2481 	{
2482 		if (tTd(48, 2))
2483 			printf("rscheck: discard mailer selected\n");
2484 		e->e_flags |= EF_DISCARD;
2485 		discard = TRUE;
2486 	}
2487 	else
2488 	{
2489 		int savelogusrerrs = LogUsrErrs;
2490 		static bool logged = FALSE;
2491 
2492 		/* got an error -- process it */
2493 		saveexitstat = ExitStat;
2494 		LogUsrErrs = FALSE;
2495 		(void) buildaddr(pvp, &a1, 0, e);
2496 		LogUsrErrs = savelogusrerrs;
2497 		rstat = ExitStat;
2498 		ExitStat = saveexitstat;
2499 		if (!logged)
2500 		{
2501 			markstats(e, &a1, TRUE);
2502 			logged = TRUE;
2503 		}
2504 	}
2505 
2506 	if (LogLevel >= 4)
2507 	{
2508 		char *relay;
2509 		char *p;
2510 		char lbuf[MAXLINE];
2511 
2512 		p = lbuf;
2513 		if (p2 != NULL)
2514 		{
2515 			snprintf(p, SPACELEFT(lbuf, p),
2516 				", arg2=%s",
2517 				p2);
2518 			p += strlen(p);
2519 		}
2520 		if ((relay = macvalue('_', e)) != NULL)
2521 		{
2522 			snprintf(p, SPACELEFT(lbuf, p),
2523 				", relay=%s", relay);
2524 			p += strlen(p);
2525 		}
2526 		*p = '\0';
2527 		if (discard)
2528 			sm_syslog(LOG_NOTICE, e->e_id,
2529 				  "ruleset=%s, arg1=%s%s, discard",
2530 				  rwset, p1, lbuf);
2531 		else
2532 			sm_syslog(LOG_NOTICE, e->e_id,
2533 				  "ruleset=%s, arg1=%s%s, reject=%s",
2534 				  rwset, p1, lbuf, MsgBuf);
2535 	}
2536 
2537  finis:
2538 	/* clean up */
2539 	QuickAbort = saveQuickAbort;
2540 	setstat(rstat);
2541 	if (buf != buf0)
2542 		free(buf);
2543 
2544 	if (rstat != EX_OK && QuickAbort)
2545 		longjmp(TopFrame, 2);
2546 	return rstat;
2547 }
2548