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.49 (Berkeley) 02/05/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, rstatus=%s\n",
1652 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
1653 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
1654 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
1655 
1656 		if (!follow)
1657 			return;
1658 		a = a->q_next;
1659 	}
1660 }
1661 /*
1662 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
1663 **
1664 **	Parameters:
1665 **		a -- pointer to the address
1666 **
1667 **	Returns:
1668 **		TRUE -- if this address is "empty" (i.e., no one should
1669 **			ever generate replies to it.
1670 **		FALSE -- if it is a "regular" (read: replyable) address.
1671 */
1672 
1673 bool
1674 emptyaddr(a)
1675 	register ADDRESS *a;
1676 {
1677 	return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
1678 }
1679 /*
1680 **  REMOTENAME -- return the name relative to the current mailer
1681 **
1682 **	Parameters:
1683 **		name -- the name to translate.
1684 **		m -- the mailer that we want to do rewriting relative
1685 **			to.
1686 **		flags -- fine tune operations.
1687 **		pstat -- pointer to status word.
1688 **		e -- the current envelope.
1689 **
1690 **	Returns:
1691 **		the text string representing this address relative to
1692 **			the receiving mailer.
1693 **
1694 **	Side Effects:
1695 **		none.
1696 **
1697 **	Warnings:
1698 **		The text string returned is tucked away locally;
1699 **			copy it if you intend to save it.
1700 */
1701 
1702 char *
1703 remotename(name, m, flags, pstat, e)
1704 	char *name;
1705 	struct mailer *m;
1706 	int flags;
1707 	int *pstat;
1708 	register ENVELOPE *e;
1709 {
1710 	register char **pvp;
1711 	char *fancy;
1712 	char *oldg = macvalue('g', e);
1713 	int rwset;
1714 	static char buf[MAXNAME];
1715 	char lbuf[MAXNAME];
1716 	char pvpbuf[PSBUFSIZE];
1717 	extern char *crackaddr();
1718 
1719 	if (tTd(12, 1))
1720 		printf("remotename(%s)\n", name);
1721 
1722 	/* don't do anything if we are tagging it as special */
1723 	if (bitset(RF_SENDERADDR, flags))
1724 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
1725 						     : m->m_se_rwset;
1726 	else
1727 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
1728 						     : m->m_re_rwset;
1729 	if (rwset < 0)
1730 		return (name);
1731 
1732 	/*
1733 	**  Do a heuristic crack of this name to extract any comment info.
1734 	**	This will leave the name as a comment and a $g macro.
1735 	*/
1736 
1737 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
1738 		fancy = "\201g";
1739 	else
1740 		fancy = crackaddr(name);
1741 
1742 	/*
1743 	**  Turn the name into canonical form.
1744 	**	Normally this will be RFC 822 style, i.e., "user@domain".
1745 	**	If this only resolves to "user", and the "C" flag is
1746 	**	specified in the sending mailer, then the sender's
1747 	**	domain will be appended.
1748 	*/
1749 
1750 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
1751 	if (pvp == NULL)
1752 		return (name);
1753 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
1754 		*pstat = EX_TEMPFAIL;
1755 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
1756 	{
1757 		/* append from domain to this address */
1758 		register char **pxp = pvp;
1759 
1760 		/* see if there is an "@domain" in the current name */
1761 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1762 			pxp++;
1763 		if (*pxp == NULL)
1764 		{
1765 			/* no.... append the "@domain" from the sender */
1766 			register char **qxq = e->e_fromdomain;
1767 
1768 			while ((*pxp++ = *qxq++) != NULL)
1769 				continue;
1770 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
1771 				*pstat = EX_TEMPFAIL;
1772 		}
1773 	}
1774 
1775 	/*
1776 	**  Do more specific rewriting.
1777 	**	Rewrite using ruleset 1 or 2 depending on whether this is
1778 	**		a sender address or not.
1779 	**	Then run it through any receiving-mailer-specific rulesets.
1780 	*/
1781 
1782 	if (bitset(RF_SENDERADDR, flags))
1783 	{
1784 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
1785 			*pstat = EX_TEMPFAIL;
1786 	}
1787 	else
1788 	{
1789 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
1790 			*pstat = EX_TEMPFAIL;
1791 	}
1792 	if (rwset > 0)
1793 	{
1794 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
1795 			*pstat = EX_TEMPFAIL;
1796 	}
1797 
1798 	/*
1799 	**  Do any final sanitation the address may require.
1800 	**	This will normally be used to turn internal forms
1801 	**	(e.g., user@host.LOCAL) into external form.  This
1802 	**	may be used as a default to the above rules.
1803 	*/
1804 
1805 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
1806 		*pstat = EX_TEMPFAIL;
1807 
1808 	/*
1809 	**  Now restore the comment information we had at the beginning.
1810 	*/
1811 
1812 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
1813 	define('g', lbuf, e);
1814 
1815 	/* need to make sure route-addrs have <angle brackets> */
1816 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
1817 		expand("<\201g>", buf, &buf[sizeof buf - 1], e);
1818 	else
1819 		expand(fancy, buf, &buf[sizeof buf - 1], e);
1820 
1821 	define('g', oldg, e);
1822 
1823 	if (tTd(12, 1))
1824 		printf("remotename => `%s'\n", buf);
1825 	return (buf);
1826 }
1827 /*
1828 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
1829 **
1830 **	Parameters:
1831 **		a -- the address to map (but just the user name part).
1832 **		sendq -- the sendq in which to install any replacement
1833 **			addresses.
1834 **		aliaslevel -- the alias nesting depth.
1835 **		e -- the envelope.
1836 **
1837 **	Returns:
1838 **		none.
1839 */
1840 
1841 maplocaluser(a, sendq, aliaslevel, e)
1842 	register ADDRESS *a;
1843 	ADDRESS **sendq;
1844 	int aliaslevel;
1845 	ENVELOPE *e;
1846 {
1847 	register char **pvp;
1848 	register ADDRESS *a1 = NULL;
1849 	auto char *delimptr;
1850 	char pvpbuf[PSBUFSIZE];
1851 
1852 	if (tTd(29, 1))
1853 	{
1854 		printf("maplocaluser: ");
1855 		printaddr(a, FALSE);
1856 	}
1857 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
1858 	if (pvp == NULL)
1859 		return;
1860 
1861 	(void) rewrite(pvp, 5, 0, e);
1862 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
1863 		return;
1864 
1865 	/* if non-null, mailer destination specified -- has it changed? */
1866 	a1 = buildaddr(pvp, NULL, 0, e);
1867 	if (a1 == NULL || sameaddr(a, a1))
1868 		return;
1869 
1870 	/* mark old address as dead; insert new address */
1871 	a->q_flags |= QDONTSEND;
1872 	if (tTd(29, 5))
1873 	{
1874 		printf("maplocaluser: QDONTSEND ");
1875 		printaddr(a, FALSE);
1876 	}
1877 	a1->q_alias = a;
1878 	allocaddr(a1, RF_COPYALL, NULL);
1879 	(void) recipient(a1, sendq, aliaslevel, e);
1880 }
1881 /*
1882 **  DEQUOTE_INIT -- initialize dequote map
1883 **
1884 **	This is a no-op.
1885 **
1886 **	Parameters:
1887 **		map -- the internal map structure.
1888 **		args -- arguments.
1889 **
1890 **	Returns:
1891 **		TRUE.
1892 */
1893 
1894 bool
1895 dequote_init(map, args)
1896 	MAP *map;
1897 	char *args;
1898 {
1899 	register char *p = args;
1900 
1901 	for (;;)
1902 	{
1903 		while (isascii(*p) && isspace(*p))
1904 			p++;
1905 		if (*p != '-')
1906 			break;
1907 		switch (*++p)
1908 		{
1909 		  case 'a':
1910 			map->map_app = ++p;
1911 			break;
1912 
1913 		  case 's':
1914 			map->map_coldelim = *++p;
1915 			break;
1916 		}
1917 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1918 			p++;
1919 		if (*p != '\0')
1920 			*p = '\0';
1921 	}
1922 	if (map->map_app != NULL)
1923 		map->map_app = newstr(map->map_app);
1924 
1925 	return TRUE;
1926 }
1927 /*
1928 **  DEQUOTE_MAP -- unquote an address
1929 **
1930 **	Parameters:
1931 **		map -- the internal map structure (ignored).
1932 **		name -- the name to dequote.
1933 **		av -- arguments (ignored).
1934 **		statp -- pointer to status out-parameter.
1935 **
1936 **	Returns:
1937 **		NULL -- if there were no quotes, or if the resulting
1938 **			unquoted buffer would not be acceptable to prescan.
1939 **		else -- The dequoted buffer.
1940 */
1941 
1942 char *
1943 dequote_map(map, name, av, statp)
1944 	MAP *map;
1945 	char *name;
1946 	char **av;
1947 	int *statp;
1948 {
1949 	register char *p;
1950 	register char *q;
1951 	register char c;
1952 	int anglecnt = 0;
1953 	int cmntcnt = 0;
1954 	int quotecnt = 0;
1955 	int spacecnt = 0;
1956 	bool quotemode = FALSE;
1957 	bool bslashmode = FALSE;
1958 	char spacesub = map->map_coldelim;
1959 
1960 	for (p = q = name; (c = *p++) != '\0'; )
1961 	{
1962 		if (bslashmode)
1963 		{
1964 			bslashmode = FALSE;
1965 			*q++ = c;
1966 			continue;
1967 		}
1968 
1969 		if (c == ' ' && spacesub != '\0')
1970 			c = spacesub;
1971 
1972 		switch (c)
1973 		{
1974 		  case '\\':
1975 			bslashmode = TRUE;
1976 			break;
1977 
1978 		  case '(':
1979 			cmntcnt++;
1980 			break;
1981 
1982 		  case ')':
1983 			if (cmntcnt-- <= 0)
1984 				return NULL;
1985 			break;
1986 
1987 		  case ' ':
1988 			spacecnt++;
1989 			break;
1990 		}
1991 
1992 		if (cmntcnt > 0)
1993 		{
1994 			*q++ = c;
1995 			continue;
1996 		}
1997 
1998 		switch (c)
1999 		{
2000 		  case '"':
2001 			quotemode = !quotemode;
2002 			quotecnt++;
2003 			continue;
2004 
2005 		  case '<':
2006 			anglecnt++;
2007 			break;
2008 
2009 		  case '>':
2010 			if (anglecnt-- <= 0)
2011 				return NULL;
2012 			break;
2013 		}
2014 		*q++ = c;
2015 	}
2016 
2017 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2018 	    quotemode || quotecnt <= 0 || spacecnt != 0)
2019 		return NULL;
2020 	*q++ = '\0';
2021 	return name;
2022 }
2023