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