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