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