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.52 (Berkeley) 02/20/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 = "4.4.3";
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 	int 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 				int ruleset;
1158 				STAB *s;
1159 
1160 				bcopy((char *) &npvp[2], (char *) pvp,
1161 					(int) (avp - npvp - 2) * sizeof *avp);
1162 				if (tTd(21, 3))
1163 					printf("-----callsubr %s\n", npvp[1]);
1164 				s = stab(npvp[1], ST_RULESET, ST_FIND);
1165 				if (s == NULL)
1166 					ruleset = atoi(npvp[1]);
1167 				else
1168 					ruleset = s->s_ruleset;
1169 				stat = rewrite(pvp, ruleset, reclevel, e);
1170 				if (rstat == EX_OK || stat == EX_TEMPFAIL)
1171 					rstat = stat;
1172 				if (*pvp != NULL && (**pvp & 0377) == CANONNET)
1173 				rwr = NULL;
1174 			}
1175 		}
1176 		else
1177 		{
1178 			bcopy((char *) npvp, (char *) pvp,
1179 				(int) (avp - npvp) * sizeof *avp);
1180 		}
1181 		if (tTd(21, 4))
1182 		{
1183 			printf("rewritten as:");
1184 			printav(pvp);
1185 		}
1186 	}
1187 
1188 	if (OpMode == MD_TEST || tTd(21, 1))
1189 	{
1190 		printf("rewrite: ruleset %2d returns:", ruleset);
1191 		printav(pvp);
1192 	}
1193 
1194 	return rstat;
1195 }
1196 /*
1197 **  BUILDADDR -- build address from token vector.
1198 **
1199 **	Parameters:
1200 **		tv -- token vector.
1201 **		a -- pointer to address descriptor to fill.
1202 **			If NULL, one will be allocated.
1203 **		flags -- info regarding whether this is a sender or
1204 **			a recipient.
1205 **		e -- the current envelope.
1206 **
1207 **	Returns:
1208 **		NULL if there was an error.
1209 **		'a' otherwise.
1210 **
1211 **	Side Effects:
1212 **		fills in 'a'
1213 */
1214 
1215 struct errcodes
1216 {
1217 	char	*ec_name;		/* name of error code */
1218 	int	ec_code;		/* numeric code */
1219 } ErrorCodes[] =
1220 {
1221 	"usage",	EX_USAGE,
1222 	"nouser",	EX_NOUSER,
1223 	"nohost",	EX_NOHOST,
1224 	"unavailable",	EX_UNAVAILABLE,
1225 	"software",	EX_SOFTWARE,
1226 	"tempfail",	EX_TEMPFAIL,
1227 	"protocol",	EX_PROTOCOL,
1228 #ifdef EX_CONFIG
1229 	"config",	EX_CONFIG,
1230 #endif
1231 	NULL,		EX_UNAVAILABLE,
1232 };
1233 
1234 ADDRESS *
1235 buildaddr(tv, a, flags, e)
1236 	register char **tv;
1237 	register ADDRESS *a;
1238 	int flags;
1239 	register ENVELOPE *e;
1240 {
1241 	struct mailer **mp;
1242 	register struct mailer *m;
1243 	char *bp;
1244 	int spaceleft;
1245 	static MAILER errormailer;
1246 	static char *errorargv[] = { "ERROR", NULL };
1247 	static char buf[MAXNAME];
1248 
1249 	if (tTd(24, 5))
1250 	{
1251 		printf("buildaddr, flags=%x, tv=", flags);
1252 		printav(tv);
1253 	}
1254 
1255 	if (a == NULL)
1256 		a = (ADDRESS *) xalloc(sizeof *a);
1257 	bzero((char *) a, sizeof *a);
1258 
1259 	/* set up default error return flags */
1260 	a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
1261 
1262 	/* figure out what net/mailer to use */
1263 	if (*tv == NULL || (**tv & 0377) != CANONNET)
1264 	{
1265 		syserr("554 buildaddr: no net");
1266 badaddr:
1267 		a->q_flags |= QBADADDR;
1268 		a->q_mailer = &errormailer;
1269 		if (errormailer.m_name == NULL)
1270 		{
1271 			/* initialize the bogus mailer */
1272 			errormailer.m_name = "*error*";
1273 			errormailer.m_mailer = "ERROR";
1274 			errormailer.m_argv = errorargv;
1275 		}
1276 		return a;
1277 	}
1278 	tv++;
1279 	if (strcasecmp(*tv, "error") == 0)
1280 	{
1281 		if ((**++tv & 0377) == CANONHOST)
1282 		{
1283 			register struct errcodes *ep;
1284 
1285 			if (isascii(**++tv) && isdigit(**tv))
1286 			{
1287 				setstat(atoi(*tv));
1288 			}
1289 			else
1290 			{
1291 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1292 					if (strcasecmp(ep->ec_name, *tv) == 0)
1293 						break;
1294 				setstat(ep->ec_code);
1295 			}
1296 			tv++;
1297 		}
1298 		else
1299 			setstat(EX_UNAVAILABLE);
1300 		if ((**tv & 0377) != CANONUSER)
1301 			syserr("554 buildaddr: error: no user");
1302 		cataddr(++tv, NULL, buf, sizeof buf, ' ');
1303 		stripquotes(buf);
1304 		if (isascii(buf[0]) && isdigit(buf[0]) &&
1305 		    isascii(buf[1]) && isdigit(buf[1]) &&
1306 		    isascii(buf[2]) && isdigit(buf[2]) &&
1307 		    buf[3] == ' ')
1308 		{
1309 			char fmt[10];
1310 
1311 			strncpy(fmt, buf, 3);
1312 			strcpy(&fmt[3], " %s");
1313 			usrerr(fmt, buf + 4);
1314 
1315 			/*
1316 			**  If this is a 4xx code and we aren't running
1317 			**  SMTP on our input, bounce this message;
1318 			**  otherwise it disappears without a trace.
1319 			*/
1320 
1321 			if (fmt[0] == '4' && OpMode != MD_SMTP &&
1322 			    OpMode != MD_DAEMON)
1323 			{
1324 				e->e_flags |= EF_FATALERRS;
1325 			}
1326 		}
1327 		else
1328 		{
1329 			usrerr("553 %s", buf);
1330 		}
1331 		goto badaddr;
1332 	}
1333 
1334 	for (mp = Mailer; (m = *mp++) != NULL; )
1335 	{
1336 		if (strcasecmp(m->m_name, *tv) == 0)
1337 			break;
1338 	}
1339 	if (m == NULL)
1340 	{
1341 		syserr("554 buildaddr: unknown mailer %s", *tv);
1342 		goto badaddr;
1343 	}
1344 	a->q_mailer = m;
1345 
1346 	/* figure out what host (if any) */
1347 	tv++;
1348 	if ((**tv & 0377) == CANONHOST)
1349 	{
1350 		bp = buf;
1351 		spaceleft = sizeof buf - 1;
1352 		while (*++tv != NULL && (**tv & 0377) != CANONUSER)
1353 		{
1354 			int i = strlen(*tv);
1355 
1356 			if (i > spaceleft)
1357 			{
1358 				/* out of space for this address */
1359 				if (spaceleft >= 0)
1360 					syserr("554 buildaddr: host too long (%.40s...)",
1361 						buf);
1362 				i = spaceleft;
1363 				spaceleft = 0;
1364 			}
1365 			if (i <= 0)
1366 				continue;
1367 			bcopy(*tv, bp, i);
1368 			bp += i;
1369 			spaceleft -= i;
1370 		}
1371 		*bp = '\0';
1372 		a->q_host = newstr(buf);
1373 	}
1374 	else
1375 	{
1376 		if (!bitnset(M_LOCALMAILER, m->m_flags))
1377 		{
1378 			syserr("554 buildaddr: no host");
1379 			goto badaddr;
1380 		}
1381 		a->q_host = NULL;
1382 	}
1383 
1384 	/* figure out the user */
1385 	if (*tv == NULL || (**tv & 0377) != CANONUSER)
1386 	{
1387 		syserr("554 buildaddr: no user");
1388 		goto badaddr;
1389 	}
1390 	tv++;
1391 
1392 	if (bitnset(M_CHECKUDB, m->m_flags) && *tv != NULL &&
1393 	    strcmp(*tv, "@") == 0)
1394 	{
1395 		tv++;
1396 		a->q_flags |= QNOTREMOTE;
1397 	}
1398 
1399 	/* do special mapping for local mailer */
1400 	if (*tv != NULL)
1401 	{
1402 		register char *p = *tv;
1403 
1404 		if (*p == '"')
1405 			p++;
1406 		if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1407 			a->q_mailer = m = ProgMailer;
1408 		else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1409 			a->q_mailer = m = FileMailer;
1410 		else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
1411 		{
1412 			/* may be :include: */
1413 			cataddr(tv, NULL, buf, sizeof buf, '\0');
1414 			stripquotes(buf);
1415 			if (strncasecmp(buf, ":include:", 9) == 0)
1416 			{
1417 				/* if :include:, don't need further rewriting */
1418 				a->q_mailer = m = InclMailer;
1419 				a->q_user = &buf[9];
1420 				return (a);
1421 			}
1422 		}
1423 	}
1424 
1425 	/* rewrite according recipient mailer rewriting rules */
1426 	define('h', a->q_host, e);
1427 	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1428 	{
1429 		/* sender addresses done later */
1430 		(void) rewrite(tv, 2, 0, e);
1431 		if (m->m_re_rwset > 0)
1432 		       (void) rewrite(tv, m->m_re_rwset, 0, e);
1433 	}
1434 	(void) rewrite(tv, 4, 0, e);
1435 
1436 	/* save the result for the command line/RCPT argument */
1437 	cataddr(tv, NULL, buf, sizeof buf, '\0');
1438 	a->q_user = buf;
1439 
1440 	/*
1441 	**  Do mapping to lower case as requested by mailer
1442 	*/
1443 
1444 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1445 		makelower(a->q_host);
1446 	if (!bitnset(M_USR_UPPER, m->m_flags))
1447 		makelower(a->q_user);
1448 
1449 	return (a);
1450 }
1451 /*
1452 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1453 **
1454 **	Parameters:
1455 **		pvp -- parameter vector to rebuild.
1456 **		evp -- last parameter to include.  Can be NULL to
1457 **			use entire pvp.
1458 **		buf -- buffer to build the string into.
1459 **		sz -- size of buf.
1460 **		spacesub -- the space separator character; if null,
1461 **			use SpaceSub.
1462 **
1463 **	Returns:
1464 **		none.
1465 **
1466 **	Side Effects:
1467 **		Destroys buf.
1468 */
1469 
1470 cataddr(pvp, evp, buf, sz, spacesub)
1471 	char **pvp;
1472 	char **evp;
1473 	char *buf;
1474 	register int sz;
1475 	char spacesub;
1476 {
1477 	bool oatomtok = FALSE;
1478 	bool natomtok = FALSE;
1479 	register int i;
1480 	register char *p;
1481 
1482 	if (spacesub == '\0')
1483 		spacesub = SpaceSub;
1484 
1485 	if (pvp == NULL)
1486 	{
1487 		(void) strcpy(buf, "");
1488 		return;
1489 	}
1490 	p = buf;
1491 	sz -= 2;
1492 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1493 	{
1494 		natomtok = (toktype(**pvp) == ATM);
1495 		if (oatomtok && natomtok)
1496 			*p++ = spacesub;
1497 		(void) strcpy(p, *pvp);
1498 		oatomtok = natomtok;
1499 		p += i;
1500 		sz -= i + 1;
1501 		if (pvp++ == evp)
1502 			break;
1503 	}
1504 	*p = '\0';
1505 }
1506 /*
1507 **  SAMEADDR -- Determine if two addresses are the same
1508 **
1509 **	This is not just a straight comparison -- if the mailer doesn't
1510 **	care about the host we just ignore it, etc.
1511 **
1512 **	Parameters:
1513 **		a, b -- pointers to the internal forms to compare.
1514 **
1515 **	Returns:
1516 **		TRUE -- they represent the same mailbox.
1517 **		FALSE -- they don't.
1518 **
1519 **	Side Effects:
1520 **		none.
1521 */
1522 
1523 bool
1524 sameaddr(a, b)
1525 	register ADDRESS *a;
1526 	register ADDRESS *b;
1527 {
1528 	register ADDRESS *ca, *cb;
1529 
1530 	/* if they don't have the same mailer, forget it */
1531 	if (a->q_mailer != b->q_mailer)
1532 		return (FALSE);
1533 
1534 	/* if the user isn't the same, we can drop out */
1535 	if (strcmp(a->q_user, b->q_user) != 0)
1536 		return (FALSE);
1537 
1538 	/* if we have good uids for both but they differ, these are different */
1539 	if (a->q_mailer == ProgMailer)
1540 	{
1541 		ca = getctladdr(a);
1542 		cb = getctladdr(b);
1543 		if (ca != NULL && cb != NULL &&
1544 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1545 		    ca->q_uid != cb->q_uid)
1546 			return (FALSE);
1547 	}
1548 
1549 	/* otherwise compare hosts (but be careful for NULL ptrs) */
1550 	if (a->q_host == b->q_host)
1551 	{
1552 		/* probably both null pointers */
1553 		return (TRUE);
1554 	}
1555 	if (a->q_host == NULL || b->q_host == NULL)
1556 	{
1557 		/* only one is a null pointer */
1558 		return (FALSE);
1559 	}
1560 	if (strcmp(a->q_host, b->q_host) != 0)
1561 		return (FALSE);
1562 
1563 	return (TRUE);
1564 }
1565 /*
1566 **  PRINTADDR -- print address (for debugging)
1567 **
1568 **	Parameters:
1569 **		a -- the address to print
1570 **		follow -- follow the q_next chain.
1571 **
1572 **	Returns:
1573 **		none.
1574 **
1575 **	Side Effects:
1576 **		none.
1577 */
1578 
1579 struct qflags
1580 {
1581 	char	*qf_name;
1582 	u_long	qf_bit;
1583 };
1584 
1585 struct qflags	AddressFlags[] =
1586 {
1587 	"QDONTSEND",		QDONTSEND,
1588 	"QBADADDR",		QBADADDR,
1589 	"QGOODUID",		QGOODUID,
1590 	"QPRIMARY",		QPRIMARY,
1591 	"QQUEUEUP",		QQUEUEUP,
1592 	"QSENT",		QSENT,
1593 	"QNOTREMOTE",		QNOTREMOTE,
1594 	"QSELFREF",		QSELFREF,
1595 	"QVERIFIED",		QVERIFIED,
1596 	"QREPORT",		QREPORT,
1597 	"QBOGUSSHELL",		QBOGUSSHELL,
1598 	"QUNSAFEADDR",		QUNSAFEADDR,
1599 	"QPINGONSUCCESS",	QPINGONSUCCESS,
1600 	"QPINGONFAILURE",	QPINGONFAILURE,
1601 	"QPINGONDELAY",		QPINGONDELAY,
1602 	"QHAS_RET_PARAM",	QHAS_RET_PARAM,
1603 	"QRET_HDRS",		QRET_HDRS,
1604 	"QRELAYED",		QRELAYED,
1605 	NULL
1606 };
1607 
1608 printaddr(a, follow)
1609 	register ADDRESS *a;
1610 	bool follow;
1611 {
1612 	register MAILER *m;
1613 	MAILER pseudomailer;
1614 	register struct qflags *qfp;
1615 	bool firstone;
1616 
1617 	if (a == NULL)
1618 	{
1619 		printf("[NULL]\n");
1620 		return;
1621 	}
1622 
1623 	while (a != NULL)
1624 	{
1625 		printf("%x=", a);
1626 		(void) fflush(stdout);
1627 
1628 		/* find the mailer -- carefully */
1629 		m = a->q_mailer;
1630 		if (m == NULL)
1631 		{
1632 			m = &pseudomailer;
1633 			m->m_mno = -1;
1634 			m->m_name = "NULL";
1635 		}
1636 
1637 		printf("%s:\n\tmailer %d (%s), host `%s', user `%s', ruser `%s'\n",
1638 		       a->q_paddr, m->m_mno, m->m_name,
1639 		       a->q_host == NULL ? "<null>" : a->q_host, a->q_user,
1640 		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
1641 		printf("\tnext=%x, alias %x, uid %d, gid %d\n",
1642 		       a->q_next, a->q_alias, a->q_uid, a->q_gid);
1643 		printf("\tflags=%lx<", a->q_flags);
1644 		firstone = TRUE;
1645 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
1646 		{
1647 			if (!bitset(qfp->qf_bit, a->q_flags))
1648 				continue;
1649 			if (!firstone)
1650 				printf(",");
1651 			firstone = FALSE;
1652 			printf("%s", qfp->qf_name);
1653 		}
1654 		printf(">\n");
1655 		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
1656 		       a->q_owner == NULL ? "(none)" : a->q_owner,
1657 		       a->q_home == NULL ? "(none)" : a->q_home,
1658 		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
1659 		printf("\torcpt=\"%s\", statmta=%s, rstatus=%s\n",
1660 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
1661 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
1662 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
1663 
1664 		if (!follow)
1665 			return;
1666 		a = a->q_next;
1667 	}
1668 }
1669 /*
1670 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
1671 **
1672 **	Parameters:
1673 **		a -- pointer to the address
1674 **
1675 **	Returns:
1676 **		TRUE -- if this address is "empty" (i.e., no one should
1677 **			ever generate replies to it.
1678 **		FALSE -- if it is a "regular" (read: replyable) address.
1679 */
1680 
1681 bool
1682 emptyaddr(a)
1683 	register ADDRESS *a;
1684 {
1685 	return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
1686 }
1687 /*
1688 **  REMOTENAME -- return the name relative to the current mailer
1689 **
1690 **	Parameters:
1691 **		name -- the name to translate.
1692 **		m -- the mailer that we want to do rewriting relative
1693 **			to.
1694 **		flags -- fine tune operations.
1695 **		pstat -- pointer to status word.
1696 **		e -- the current envelope.
1697 **
1698 **	Returns:
1699 **		the text string representing this address relative to
1700 **			the receiving mailer.
1701 **
1702 **	Side Effects:
1703 **		none.
1704 **
1705 **	Warnings:
1706 **		The text string returned is tucked away locally;
1707 **			copy it if you intend to save it.
1708 */
1709 
1710 char *
1711 remotename(name, m, flags, pstat, e)
1712 	char *name;
1713 	struct mailer *m;
1714 	int flags;
1715 	int *pstat;
1716 	register ENVELOPE *e;
1717 {
1718 	register char **pvp;
1719 	char *fancy;
1720 	char *oldg = macvalue('g', e);
1721 	int rwset;
1722 	static char buf[MAXNAME];
1723 	char lbuf[MAXNAME];
1724 	char pvpbuf[PSBUFSIZE];
1725 	extern char *crackaddr();
1726 
1727 	if (tTd(12, 1))
1728 		printf("remotename(%s)\n", name);
1729 
1730 	/* don't do anything if we are tagging it as special */
1731 	if (bitset(RF_SENDERADDR, flags))
1732 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
1733 						     : m->m_se_rwset;
1734 	else
1735 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
1736 						     : m->m_re_rwset;
1737 	if (rwset < 0)
1738 		return (name);
1739 
1740 	/*
1741 	**  Do a heuristic crack of this name to extract any comment info.
1742 	**	This will leave the name as a comment and a $g macro.
1743 	*/
1744 
1745 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
1746 		fancy = "\201g";
1747 	else
1748 		fancy = crackaddr(name);
1749 
1750 	/*
1751 	**  Turn the name into canonical form.
1752 	**	Normally this will be RFC 822 style, i.e., "user@domain".
1753 	**	If this only resolves to "user", and the "C" flag is
1754 	**	specified in the sending mailer, then the sender's
1755 	**	domain will be appended.
1756 	*/
1757 
1758 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL);
1759 	if (pvp == NULL)
1760 		return (name);
1761 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
1762 		*pstat = EX_TEMPFAIL;
1763 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
1764 	{
1765 		/* append from domain to this address */
1766 		register char **pxp = pvp;
1767 
1768 		/* see if there is an "@domain" in the current name */
1769 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1770 			pxp++;
1771 		if (*pxp == NULL)
1772 		{
1773 			/* no.... append the "@domain" from the sender */
1774 			register char **qxq = e->e_fromdomain;
1775 
1776 			while ((*pxp++ = *qxq++) != NULL)
1777 				continue;
1778 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
1779 				*pstat = EX_TEMPFAIL;
1780 		}
1781 	}
1782 
1783 	/*
1784 	**  Do more specific rewriting.
1785 	**	Rewrite using ruleset 1 or 2 depending on whether this is
1786 	**		a sender address or not.
1787 	**	Then run it through any receiving-mailer-specific rulesets.
1788 	*/
1789 
1790 	if (bitset(RF_SENDERADDR, flags))
1791 	{
1792 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
1793 			*pstat = EX_TEMPFAIL;
1794 	}
1795 	else
1796 	{
1797 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
1798 			*pstat = EX_TEMPFAIL;
1799 	}
1800 	if (rwset > 0)
1801 	{
1802 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
1803 			*pstat = EX_TEMPFAIL;
1804 	}
1805 
1806 	/*
1807 	**  Do any final sanitation the address may require.
1808 	**	This will normally be used to turn internal forms
1809 	**	(e.g., user@host.LOCAL) into external form.  This
1810 	**	may be used as a default to the above rules.
1811 	*/
1812 
1813 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
1814 		*pstat = EX_TEMPFAIL;
1815 
1816 	/*
1817 	**  Now restore the comment information we had at the beginning.
1818 	*/
1819 
1820 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
1821 	define('g', lbuf, e);
1822 
1823 	/* need to make sure route-addrs have <angle brackets> */
1824 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
1825 		expand("<\201g>", buf, &buf[sizeof buf - 1], e);
1826 	else
1827 		expand(fancy, buf, &buf[sizeof buf - 1], e);
1828 
1829 	define('g', oldg, e);
1830 
1831 	if (tTd(12, 1))
1832 		printf("remotename => `%s'\n", buf);
1833 	return (buf);
1834 }
1835 /*
1836 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
1837 **
1838 **	Parameters:
1839 **		a -- the address to map (but just the user name part).
1840 **		sendq -- the sendq in which to install any replacement
1841 **			addresses.
1842 **		aliaslevel -- the alias nesting depth.
1843 **		e -- the envelope.
1844 **
1845 **	Returns:
1846 **		none.
1847 */
1848 
1849 maplocaluser(a, sendq, aliaslevel, e)
1850 	register ADDRESS *a;
1851 	ADDRESS **sendq;
1852 	int aliaslevel;
1853 	ENVELOPE *e;
1854 {
1855 	register char **pvp;
1856 	register ADDRESS *a1 = NULL;
1857 	auto char *delimptr;
1858 	char pvpbuf[PSBUFSIZE];
1859 
1860 	if (tTd(29, 1))
1861 	{
1862 		printf("maplocaluser: ");
1863 		printaddr(a, FALSE);
1864 	}
1865 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr);
1866 	if (pvp == NULL)
1867 		return;
1868 
1869 	(void) rewrite(pvp, 5, 0, e);
1870 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
1871 		return;
1872 
1873 	/* if non-null, mailer destination specified -- has it changed? */
1874 	a1 = buildaddr(pvp, NULL, 0, e);
1875 	if (a1 == NULL || sameaddr(a, a1))
1876 		return;
1877 
1878 	/* mark old address as dead; insert new address */
1879 	a->q_flags |= QDONTSEND;
1880 	if (tTd(29, 5))
1881 	{
1882 		printf("maplocaluser: QDONTSEND ");
1883 		printaddr(a, FALSE);
1884 	}
1885 	a1->q_alias = a;
1886 	allocaddr(a1, RF_COPYALL, NULL);
1887 	(void) recipient(a1, sendq, aliaslevel, e);
1888 }
1889 /*
1890 **  DEQUOTE_INIT -- initialize dequote map
1891 **
1892 **	This is a no-op.
1893 **
1894 **	Parameters:
1895 **		map -- the internal map structure.
1896 **		args -- arguments.
1897 **
1898 **	Returns:
1899 **		TRUE.
1900 */
1901 
1902 bool
1903 dequote_init(map, args)
1904 	MAP *map;
1905 	char *args;
1906 {
1907 	register char *p = args;
1908 
1909 	for (;;)
1910 	{
1911 		while (isascii(*p) && isspace(*p))
1912 			p++;
1913 		if (*p != '-')
1914 			break;
1915 		switch (*++p)
1916 		{
1917 		  case 'a':
1918 			map->map_app = ++p;
1919 			break;
1920 
1921 		  case 's':
1922 			map->map_coldelim = *++p;
1923 			break;
1924 		}
1925 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1926 			p++;
1927 		if (*p != '\0')
1928 			*p = '\0';
1929 	}
1930 	if (map->map_app != NULL)
1931 		map->map_app = newstr(map->map_app);
1932 
1933 	return TRUE;
1934 }
1935 /*
1936 **  DEQUOTE_MAP -- unquote an address
1937 **
1938 **	Parameters:
1939 **		map -- the internal map structure (ignored).
1940 **		name -- the name to dequote.
1941 **		av -- arguments (ignored).
1942 **		statp -- pointer to status out-parameter.
1943 **
1944 **	Returns:
1945 **		NULL -- if there were no quotes, or if the resulting
1946 **			unquoted buffer would not be acceptable to prescan.
1947 **		else -- The dequoted buffer.
1948 */
1949 
1950 char *
1951 dequote_map(map, name, av, statp)
1952 	MAP *map;
1953 	char *name;
1954 	char **av;
1955 	int *statp;
1956 {
1957 	register char *p;
1958 	register char *q;
1959 	register char c;
1960 	int anglecnt = 0;
1961 	int cmntcnt = 0;
1962 	int quotecnt = 0;
1963 	int spacecnt = 0;
1964 	bool quotemode = FALSE;
1965 	bool bslashmode = FALSE;
1966 	char spacesub = map->map_coldelim;
1967 
1968 	for (p = q = name; (c = *p++) != '\0'; )
1969 	{
1970 		if (bslashmode)
1971 		{
1972 			bslashmode = FALSE;
1973 			*q++ = c;
1974 			continue;
1975 		}
1976 
1977 		if (c == ' ' && spacesub != '\0')
1978 			c = spacesub;
1979 
1980 		switch (c)
1981 		{
1982 		  case '\\':
1983 			bslashmode = TRUE;
1984 			break;
1985 
1986 		  case '(':
1987 			cmntcnt++;
1988 			break;
1989 
1990 		  case ')':
1991 			if (cmntcnt-- <= 0)
1992 				return NULL;
1993 			break;
1994 
1995 		  case ' ':
1996 			spacecnt++;
1997 			break;
1998 		}
1999 
2000 		if (cmntcnt > 0)
2001 		{
2002 			*q++ = c;
2003 			continue;
2004 		}
2005 
2006 		switch (c)
2007 		{
2008 		  case '"':
2009 			quotemode = !quotemode;
2010 			quotecnt++;
2011 			continue;
2012 
2013 		  case '<':
2014 			anglecnt++;
2015 			break;
2016 
2017 		  case '>':
2018 			if (anglecnt-- <= 0)
2019 				return NULL;
2020 			break;
2021 		}
2022 		*q++ = c;
2023 	}
2024 
2025 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2026 	    quotemode || quotecnt <= 0 || spacecnt != 0)
2027 		return NULL;
2028 	*q++ = '\0';
2029 	return name;
2030 }
2031