1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)parseaddr.c	6.6 (Berkeley) 01/13/93";
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 **		copyf -- determines what shall be copied:
34 **			-1 -- don't copy anything.  The printname
35 **				(q_paddr) is just addr, and the
36 **				user & host are allocated internally
37 **				to parse.
38 **			0 -- copy out the parsed user & host, but
39 **				don't copy the printname.
40 **			+1 -- copy everything.
41 **		delim -- the character to terminate the address, passed
42 **			to prescan.
43 **		e -- the envelope that will contain this address.
44 **
45 **	Returns:
46 **		A pointer to the address descriptor header (`a' if
47 **			`a' is non-NULL).
48 **		NULL on error.
49 **
50 **	Side Effects:
51 **		none
52 */
53 
54 /* following delimiters are inherent to the internal algorithms */
55 # define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
56 
57 ADDRESS *
58 parseaddr(addr, a, copyf, delim, e)
59 	char *addr;
60 	register ADDRESS *a;
61 	int copyf;
62 	char delim;
63 	register ENVELOPE *e;
64 {
65 	register char **pvp;
66 	char pvpbuf[PSBUFSIZE];
67 	extern char **prescan();
68 	extern ADDRESS *buildaddr();
69 	extern bool invalidaddr();
70 
71 	/*
72 	**  Initialize and prescan address.
73 	*/
74 
75 	e->e_to = addr;
76 	if (tTd(20, 1))
77 		printf("\n--parseaddr(%s)\n", addr);
78 
79 	if (invalidaddr(addr))
80 	{
81 		if (tTd(20, 1))
82 			printf("parseaddr-->bad address\n");
83 		return NULL;
84 	}
85 
86 	pvp = prescan(addr, delim, pvpbuf);
87 	if (pvp == NULL)
88 	{
89 		if (tTd(20, 1))
90 			printf("parseaddr-->NULL\n");
91 		return (NULL);
92 	}
93 
94 	/*
95 	**  Apply rewriting rules.
96 	**	Ruleset 0 does basic parsing.  It must resolve.
97 	*/
98 
99 	rewrite(pvp, 3);
100 	rewrite(pvp, 0);
101 
102 	/*
103 	**  See if we resolved to a real mailer.
104 	*/
105 
106 	if (pvp[0][0] != CANONNET)
107 	{
108 		setstat(EX_USAGE);
109 		usrerr("cannot resolve name");
110 		return (NULL);
111 	}
112 
113 	/*
114 	**  Build canonical address from pvp.
115 	*/
116 
117 	a = buildaddr(pvp, a);
118 	if (a == NULL)
119 		return (NULL);
120 
121 	/*
122 	**  Make local copies of the host & user and then
123 	**  transport them out.
124 	*/
125 
126 	allocaddr(a, copyf, addr);
127 
128 	/*
129 	**  Compute return value.
130 	*/
131 
132 	if (tTd(20, 1))
133 	{
134 		printf("parseaddr-->");
135 		printaddr(a, FALSE);
136 	}
137 
138 	return (a);
139 }
140 /*
141 **  INVALIDADDR -- check for address containing meta-characters
142 **
143 **	Parameters:
144 **		addr -- the address to check.
145 **
146 **	Returns:
147 **		TRUE -- if the address has any "wierd" characters
148 **		FALSE -- otherwise.
149 */
150 
151 bool
152 invalidaddr(addr)
153 	register char *addr;
154 {
155 	for (; *addr != '\0'; addr++)
156 	{
157 		if (!isascii((int) *addr & 0377) ||
158 		    !iscntrl(*addr) || isspace(*addr))
159 			continue;
160 		setstat(EX_USAGE);
161 		usrerr("Address contained invalid control characters");
162 		return TRUE;
163 	}
164 	return FALSE;
165 }
166 /*
167 **  ALLOCADDR -- do local allocations of address on demand.
168 **
169 **	Also lowercases the host name if requested.
170 **
171 **	Parameters:
172 **		a -- the address to reallocate.
173 **		copyf -- the copy flag (see parseaddr for description).
174 **		paddr -- the printname of the address.
175 **
176 **	Returns:
177 **		none.
178 **
179 **	Side Effects:
180 **		Copies portions of a into local buffers as requested.
181 */
182 
183 allocaddr(a, copyf, paddr)
184 	register ADDRESS *a;
185 	int copyf;
186 	char *paddr;
187 {
188 	register MAILER *m = a->q_mailer;
189 
190 	if (copyf > 0 && paddr != NULL)
191 	{
192 		extern char *DelimChar;
193 		char savec = *DelimChar;
194 
195 		*DelimChar = '\0';
196 		a->q_paddr = newstr(paddr);
197 		*DelimChar = savec;
198 	}
199 	else
200 		a->q_paddr = paddr;
201 
202 	if (a->q_user == NULL)
203 		a->q_user = "";
204 	if (a->q_host == NULL)
205 		a->q_host = "";
206 
207 	if (copyf >= 0)
208 	{
209 		a->q_host = newstr(a->q_host);
210 		if (a->q_user != a->q_paddr)
211 			a->q_user = newstr(a->q_user);
212 	}
213 
214 	if (a->q_paddr == NULL)
215 		a->q_paddr = a->q_user;
216 
217 	/*
218 	**  Convert host name to lower case if requested.
219 	**	User name will be done later.
220 	*/
221 
222 	if (!bitnset(M_HST_UPPER, m->m_flags))
223 		makelower(a->q_host);
224 }
225 /*
226 **  LOWERADDR -- map UPPER->lower case on addresses as requested.
227 **
228 **	Parameters:
229 **		a -- address to be mapped.
230 **
231 **	Returns:
232 **		none.
233 **
234 **	Side Effects:
235 **		none.
236 */
237 
238 loweraddr(a)
239 	register ADDRESS *a;
240 {
241 	register MAILER *m = a->q_mailer;
242 
243 	if (!bitnset(M_USR_UPPER, m->m_flags))
244 		makelower(a->q_user);
245 }
246 /*
247 **  PRESCAN -- Prescan name and make it canonical
248 **
249 **	Scans a name and turns it into a set of tokens.  This process
250 **	deletes blanks and comments (in parentheses).
251 **
252 **	This routine knows about quoted strings and angle brackets.
253 **
254 **	There are certain subtleties to this routine.  The one that
255 **	comes to mind now is that backslashes on the ends of names
256 **	are silently stripped off; this is intentional.  The problem
257 **	is that some versions of sndmsg (like at LBL) set the kill
258 **	character to something other than @ when reading addresses;
259 **	so people type "csvax.eric\@berkeley" -- which screws up the
260 **	berknet mailer.
261 **
262 **	Parameters:
263 **		addr -- the name to chomp.
264 **		delim -- the delimiter for the address, normally
265 **			'\0' or ','; \0 is accepted in any case.
266 **			If '\t' then we are reading the .cf file.
267 **		pvpbuf -- place to put the saved text -- note that
268 **			the pointers are static.
269 **
270 **	Returns:
271 **		A pointer to a vector of tokens.
272 **		NULL on error.
273 **
274 **	Side Effects:
275 **		sets DelimChar to point to the character matching 'delim'.
276 */
277 
278 /* states and character types */
279 # define OPR		0	/* operator */
280 # define ATM		1	/* atom */
281 # define QST		2	/* in quoted string */
282 # define SPC		3	/* chewing up spaces */
283 # define ONE		4	/* pick up one character */
284 
285 # define NSTATES	5	/* number of states */
286 # define TYPE		017	/* mask to select state type */
287 
288 /* meta bits for table */
289 # define M		020	/* meta character; don't pass through */
290 # define B		040	/* cause a break */
291 # define MB		M|B	/* meta-break */
292 
293 static short StateTab[NSTATES][NSTATES] =
294 {
295    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
296 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
297 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
298 	/*QST*/		QST,	QST,	OPR,	QST,	QST,
299 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
300 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
301 };
302 
303 # define NOCHAR		-1	/* signal nothing in lookahead token */
304 
305 char	*DelimChar;		/* set to point to the delimiter */
306 
307 char **
308 prescan(addr, delim, pvpbuf)
309 	char *addr;
310 	char delim;
311 	char pvpbuf[];
312 {
313 	register char *p;
314 	register char *q;
315 	register int c;
316 	char **avp;
317 	bool bslashmode;
318 	int cmntcnt;
319 	int anglecnt;
320 	char *tok;
321 	int state;
322 	int newstate;
323 	static char *av[MAXATOM+1];
324 	extern int errno;
325 
326 	/* make sure error messages don't have garbage on them */
327 	errno = 0;
328 
329 	q = pvpbuf;
330 	bslashmode = FALSE;
331 	cmntcnt = 0;
332 	anglecnt = 0;
333 	avp = av;
334 	state = ATM;
335 	c = NOCHAR;
336 	p = addr;
337 	if (tTd(22, 11))
338 	{
339 		printf("prescan: ");
340 		xputs(p);
341 		(void) putchar('\n');
342 	}
343 
344 	do
345 	{
346 		/* read a token */
347 		tok = q;
348 		for (;;)
349 		{
350 			/* store away any old lookahead character */
351 			if (c != NOCHAR)
352 			{
353 				/* see if there is room */
354 				if (q >= &pvpbuf[PSBUFSIZE - 5])
355 				{
356 					usrerr("Address too long");
357 					DelimChar = p;
358 					return (NULL);
359 				}
360 
361 				/* squirrel it away */
362 				*q++ = c;
363 			}
364 
365 			/* read a new input character */
366 			c = *p++;
367 			if (c == '\0' ||
368 			    (c == delim && anglecnt <= 0 && cmntcnt <= 0))
369 			{
370 				/* diagnose and patch up bad syntax */
371 				if (state == QST)
372 				{
373 					usrerr("Unbalanced '\"'");
374 					c = '"';
375 				}
376 				else if (cmntcnt > 0)
377 				{
378 					usrerr("Unbalanced '('");
379 					c = ')';
380 				}
381 				else if (anglecnt > 0)
382 				{
383 					c = '>';
384 					usrerr("Unbalanced '<'");
385 				}
386 				else
387 					break;
388 
389 				p--;
390 			}
391 
392 			if (tTd(22, 101))
393 				printf("c=%c, s=%d; ", c, state);
394 
395 			/* chew up special characters */
396 			*q = '\0';
397 			if (bslashmode)
398 			{
399 				/* kludge \! for naive users */
400 				if (c != '!')
401 					*q++ = '\\';
402 				bslashmode = FALSE;
403 				continue;
404 			}
405 
406 			if (c == '\\')
407 			{
408 				bslashmode = TRUE;
409 				c = NOCHAR;
410 				continue;
411 			}
412 			else if (state == QST)
413 			{
414 				/* do nothing, just avoid next clauses */
415 			}
416 			else if (c == '(')
417 			{
418 				cmntcnt++;
419 				c = NOCHAR;
420 			}
421 			else if (c == ')')
422 			{
423 				if (cmntcnt <= 0)
424 				{
425 					usrerr("Unbalanced ')'");
426 					DelimChar = p;
427 					return (NULL);
428 				}
429 				else
430 					cmntcnt--;
431 			}
432 			else if (cmntcnt > 0)
433 				c = NOCHAR;
434 			else if (c == '<')
435 				anglecnt++;
436 			else if (c == '>')
437 			{
438 				if (anglecnt <= 0)
439 				{
440 					usrerr("Unbalanced '>'");
441 					DelimChar = p;
442 					return (NULL);
443 				}
444 				anglecnt--;
445 			}
446 			else if (delim == ' ' && isspace(c))
447 				c = ' ';
448 
449 			if (c == NOCHAR)
450 				continue;
451 
452 			/* see if this is end of input */
453 			if (c == delim && anglecnt <= 0 && state != QST)
454 				break;
455 
456 			newstate = StateTab[state][toktype(c)];
457 			if (tTd(22, 101))
458 				printf("ns=%02o\n", newstate);
459 			state = newstate & TYPE;
460 			if (bitset(M, newstate))
461 				c = NOCHAR;
462 			if (bitset(B, newstate))
463 				break;
464 		}
465 
466 		/* new token */
467 		if (tok != q)
468 		{
469 			*q++ = '\0';
470 			if (tTd(22, 36))
471 			{
472 				printf("tok=");
473 				xputs(tok);
474 				(void) putchar('\n');
475 			}
476 			if (avp >= &av[MAXATOM])
477 			{
478 				syserr("prescan: too many tokens");
479 				DelimChar = p;
480 				return (NULL);
481 			}
482 			*avp++ = tok;
483 		}
484 	} while (c != '\0' && (c != delim || anglecnt > 0));
485 	*avp = NULL;
486 	DelimChar = --p;
487 	if (tTd(22, 12))
488 	{
489 		printf("prescan==>");
490 		printav(av);
491 	}
492 	if (av[0] != NULL)
493 		return (av);
494 	return (NULL);
495 }
496 /*
497 **  TOKTYPE -- return token type
498 **
499 **	Parameters:
500 **		c -- the character in question.
501 **
502 **	Returns:
503 **		Its type.
504 **
505 **	Side Effects:
506 **		none.
507 */
508 
509 toktype(c)
510 	register char c;
511 {
512 	static char buf[50];
513 	static bool firstime = TRUE;
514 
515 	if (firstime)
516 	{
517 		firstime = FALSE;
518 		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
519 		(void) strcat(buf, DELIMCHARS);
520 	}
521 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
522 		return (ONE);
523 	if (c == '"')
524 		return (QST);
525 	if (!isascii(c))
526 		return (ATM);
527 	if (isspace(c) || c == ')')
528 		return (SPC);
529 	if (iscntrl(c) || strchr(buf, c) != NULL)
530 		return (OPR);
531 	return (ATM);
532 }
533 /*
534 **  REWRITE -- apply rewrite rules to token vector.
535 **
536 **	This routine is an ordered production system.  Each rewrite
537 **	rule has a LHS (called the pattern) and a RHS (called the
538 **	rewrite); 'rwr' points the the current rewrite rule.
539 **
540 **	For each rewrite rule, 'avp' points the address vector we
541 **	are trying to match against, and 'pvp' points to the pattern.
542 **	If pvp points to a special match value (MATCHZANY, MATCHANY,
543 **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
544 **	matched is saved away in the match vector (pointed to by 'mvp').
545 **
546 **	When a match between avp & pvp does not match, we try to
547 **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
548 **	we must also back out the match in mvp.  If we reach a
549 **	MATCHANY or MATCHZANY we just extend the match and start
550 **	over again.
551 **
552 **	When we finally match, we rewrite the address vector
553 **	and try over again.
554 **
555 **	Parameters:
556 **		pvp -- pointer to token vector.
557 **
558 **	Returns:
559 **		none.
560 **
561 **	Side Effects:
562 **		pvp is modified.
563 */
564 
565 struct match
566 {
567 	char	**first;	/* first token matched */
568 	char	**last;		/* last token matched */
569 };
570 
571 # define MAXMATCH	9	/* max params per rewrite */
572 
573 
574 rewrite(pvp, ruleset)
575 	char **pvp;
576 	int ruleset;
577 {
578 	register char *ap;		/* address pointer */
579 	register char *rp;		/* rewrite pointer */
580 	register char **avp;		/* address vector pointer */
581 	register char **rvp;		/* rewrite vector pointer */
582 	register struct match *mlp;	/* cur ptr into mlist */
583 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
584 	struct match mlist[MAXMATCH];	/* stores match on LHS */
585 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
586 
587 	if (OpMode == MD_TEST || tTd(21, 2))
588 	{
589 		printf("rewrite: ruleset %2d   input:", ruleset);
590 		printav(pvp);
591 	}
592 	if (ruleset < 0 || ruleset >= MAXRWSETS)
593 	{
594 		syserr("rewrite: illegal ruleset number %d", ruleset);
595 		return;
596 	}
597 	if (pvp == NULL)
598 		return;
599 
600 	/*
601 	**  Run through the list of rewrite rules, applying
602 	**	any that match.
603 	*/
604 
605 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
606 	{
607 		int loopcount = 0;
608 
609 		if (tTd(21, 12))
610 		{
611 			printf("-----trying rule:");
612 			printav(rwr->r_lhs);
613 		}
614 
615 		/* try to match on this rule */
616 		mlp = mlist;
617 		rvp = rwr->r_lhs;
618 		avp = pvp;
619 		while ((ap = *avp) != NULL || *rvp != NULL)
620 		{
621 			if (++loopcount > 100)
622 			{
623 				syserr("Infinite loop in ruleset %d", ruleset);
624 				printf("workspace: ");
625 				printav(pvp);
626 				break;
627 			}
628 			rp = *rvp;
629 			if (tTd(21, 35))
630 			{
631 				printf("operator=");
632 				xputs(ap);
633 				printf(", token=");
634 				xputs(rp);
635 				printf("\n");
636 			}
637 			if (rp == NULL)
638 			{
639 				/* end-of-pattern before end-of-address */
640 				goto backup;
641 			}
642 			if (ap == NULL && *rp != MATCHZANY)
643 			{
644 				/* end-of-input */
645 				break;
646 			}
647 
648 			switch (*rp)
649 			{
650 				register STAB *s;
651 
652 			  case MATCHCLASS:
653 			  case MATCHNCLASS:
654 				/* match any token in (not in) a class */
655 				s = stab(ap, ST_CLASS, ST_FIND);
656 				if (s == NULL || !bitnset(rp[1], s->s_class))
657 				{
658 					if (*rp == MATCHCLASS)
659 						goto backup;
660 				}
661 				else if (*rp == MATCHNCLASS)
662 					goto backup;
663 
664 				/* explicit fall-through */
665 
666 			  case MATCHONE:
667 			  case MATCHANY:
668 				/* match exactly one token */
669 				mlp->first = avp;
670 				mlp->last = avp++;
671 				mlp++;
672 				break;
673 
674 			  case MATCHZANY:
675 				/* match zero or more tokens */
676 				mlp->first = avp;
677 				mlp->last = avp - 1;
678 				mlp++;
679 				break;
680 
681 			  default:
682 				/* must have exact match */
683 				if (strcasecmp(rp, ap))
684 					goto backup;
685 				avp++;
686 				break;
687 			}
688 
689 			/* successful match on this token */
690 			rvp++;
691 			continue;
692 
693 		  backup:
694 			/* match failed -- back up */
695 			while (--rvp >= rwr->r_lhs)
696 			{
697 				rp = *rvp;
698 				if (*rp == MATCHANY || *rp == MATCHZANY)
699 				{
700 					/* extend binding and continue */
701 					avp = ++mlp[-1].last;
702 					avp++;
703 					rvp++;
704 					break;
705 				}
706 				avp--;
707 				if (*rp == MATCHONE || *rp == MATCHCLASS ||
708 				    *rp == MATCHNCLASS)
709 				{
710 					/* back out binding */
711 					mlp--;
712 				}
713 			}
714 
715 			if (rvp < rwr->r_lhs)
716 			{
717 				/* total failure to match */
718 				break;
719 			}
720 		}
721 
722 		/*
723 		**  See if we successfully matched
724 		*/
725 
726 		if (rvp < rwr->r_lhs || *rvp != NULL)
727 		{
728 			if (tTd(21, 10))
729 				printf("----- rule fails\n");
730 			rwr = rwr->r_next;
731 			continue;
732 		}
733 
734 		rvp = rwr->r_rhs;
735 		if (tTd(21, 12))
736 		{
737 			printf("-----rule matches:");
738 			printav(rvp);
739 		}
740 
741 		rp = *rvp;
742 		if (*rp == CANONUSER)
743 		{
744 			rvp++;
745 			rwr = rwr->r_next;
746 		}
747 		else if (*rp == CANONHOST)
748 		{
749 			rvp++;
750 			rwr = NULL;
751 		}
752 		else if (*rp == CANONNET)
753 			rwr = NULL;
754 
755 		/* substitute */
756 		for (avp = npvp; *rvp != NULL; rvp++)
757 		{
758 			register struct match *m;
759 			register char **pp;
760 
761 			rp = *rvp;
762 			if (*rp == MATCHREPL)
763 			{
764 				/* substitute from LHS */
765 				m = &mlist[rp[1] - '1'];
766 				if (m < mlist || m >= mlp)
767 				{
768 					syserr("rewrite: ruleset %d: replacement $%c out of bounds",
769 						ruleset, rp[1]);
770 					return;
771 				}
772 				if (tTd(21, 15))
773 				{
774 					printf("$%c:", rp[1]);
775 					pp = m->first;
776 					while (pp <= m->last)
777 					{
778 						printf(" %x=\"", *pp);
779 						(void) fflush(stdout);
780 						printf("%s\"", *pp++);
781 					}
782 					printf("\n");
783 				}
784 				pp = m->first;
785 				while (pp <= m->last)
786 				{
787 					if (avp >= &npvp[MAXATOM])
788 					{
789 						syserr("rewrite: expansion too long");
790 						return;
791 					}
792 					*avp++ = *pp++;
793 				}
794 			}
795 			else
796 			{
797 				/* vanilla replacement */
798 				if (avp >= &npvp[MAXATOM])
799 				{
800 	toolong:
801 					syserr("rewrite: expansion too long");
802 					return;
803 				}
804 				*avp++ = rp;
805 			}
806 		}
807 		*avp++ = NULL;
808 
809 		/*
810 		**  Check for any hostname/keyword lookups.
811 		*/
812 
813 		for (rvp = npvp; *rvp != NULL; rvp++)
814 		{
815 			char **hbrvp;
816 			char **xpvp;
817 			int trsize;
818 			char *olddelimchar;
819 			char *replac;
820 			int endtoken;
821 			STAB *map;
822 			char *mapname;
823 			char **key_rvp;
824 			char **arg_rvp;
825 			char **default_rvp;
826 			char buf[MAXNAME + 1];
827 			char *pvpb1[MAXATOM + 1];
828 			char *argvect[10];
829 			char pvpbuf[PSBUFSIZE];
830 			extern char *DelimChar;
831 
832 			if (**rvp != HOSTBEGIN && **rvp != LOOKUPBEGIN)
833 				continue;
834 
835 			/*
836 			**  Got a hostname/keyword lookup.
837 			**
838 			**	This could be optimized fairly easily.
839 			*/
840 
841 			hbrvp = rvp;
842 			if (**rvp == HOSTBEGIN)
843 			{
844 				endtoken = HOSTEND;
845 				mapname = "host";
846 			}
847 			else
848 			{
849 				endtoken = LOOKUPEND;
850 				mapname = *++rvp;
851 			}
852 			map = stab(mapname, ST_MAP, ST_FIND);
853 			if (map == NULL)
854 				syserr("rewrite: map %s not found", mapname);
855 
856 			/* extract the match part */
857 			key_rvp = ++rvp;
858 			default_rvp = NULL;
859 			arg_rvp = argvect;
860 			xpvp = NULL;
861 			replac = pvpbuf;
862 			while (*rvp != NULL && **rvp != endtoken)
863 			{
864 				int nodetype = **rvp;
865 
866 				if (nodetype != CANONHOST && nodetype != CANONUSER)
867 				{
868 					rvp++;
869 					continue;
870 				}
871 
872 				*rvp++ = NULL;
873 
874 				if (xpvp != NULL)
875 				{
876 					cataddr(xpvp, replac,
877 						&pvpbuf[sizeof pvpbuf] - replac);
878 					*++arg_rvp = replac;
879 					replac += strlen(replac) + 1;
880 					xpvp = NULL;
881 				}
882 				switch (nodetype)
883 				{
884 				  case CANONHOST:
885 					xpvp = rvp;
886 					break;
887 
888 				  case CANONUSER:
889 					default_rvp = rvp;
890 					break;
891 				}
892 			}
893 			if (*rvp != NULL)
894 				*rvp++ = NULL;
895 			if (xpvp != NULL)
896 			{
897 				cataddr(xpvp, replac,
898 					&pvpbuf[sizeof pvpbuf] - replac);
899 				*++arg_rvp = replac;
900 			}
901 			*++arg_rvp = NULL;
902 
903 			/* save the remainder of the input string */
904 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
905 			bcopy((char *) rvp, (char *) pvpb1, trsize);
906 
907 			/* look it up */
908 			cataddr(key_rvp, buf, sizeof buf);
909 			argvect[0] = buf;
910 			if (map != NULL && bitset(MF_VALID, map->s_map.map_flags))
911 			{
912 				int bsize = sizeof buf - 1;
913 
914 				if (map->s_map.map_app != NULL)
915 					bsize -= strlen(map->s_map.map_app);
916 				replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
917 						buf, sizeof buf - 1, argvect);
918 				if (replac != NULL && map->s_map.map_app != NULL)
919 					strcat(replac, map->s_map.map_app);
920 			}
921 			else
922 				replac = NULL;
923 
924 			/* if no replacement, use default */
925 			if (replac == NULL && default_rvp != NULL)
926 			{
927 				char buf2[sizeof buf];
928 
929 				/* rewrite the default with % translations */
930 				cataddr(default_rvp, buf2, sizeof buf2);
931 				map_rewrite(buf2, sizeof buf2, buf, sizeof buf,
932 					argvect);
933 				replac = buf;
934 			}
935 
936 			if (replac == NULL)
937 			{
938 				xpvp = key_rvp;
939 			}
940 			else
941 			{
942 				/* scan the new replacement */
943 				olddelimchar = DelimChar;
944 				xpvp = prescan(replac, '\0', pvpbuf);
945 				DelimChar = olddelimchar;
946 				if (xpvp == NULL)
947 				{
948 					syserr("rewrite: cannot prescan map value: %s", replac);
949 					return;
950 				}
951 			}
952 
953 			/* append it to the token list */
954 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
955 			{
956 				*avp++ = newstr(*xpvp);
957 				if (avp >= &npvp[MAXATOM])
958 					goto toolong;
959 			}
960 
961 			/* restore the old trailing information */
962 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
963 				if (avp >= &npvp[MAXATOM])
964 					goto toolong;
965 
966 			break;
967 		}
968 
969 		/*
970 		**  Check for subroutine calls.
971 		*/
972 
973 		if (*npvp != NULL && **npvp == CALLSUBR)
974 		{
975 			bcopy((char *) &npvp[2], (char *) pvp,
976 				(int) (avp - npvp - 2) * sizeof *avp);
977 			if (tTd(21, 3))
978 				printf("-----callsubr %s\n", npvp[1]);
979 			rewrite(pvp, atoi(npvp[1]));
980 		}
981 		else
982 		{
983 			bcopy((char *) npvp, (char *) pvp,
984 				(int) (avp - npvp) * sizeof *avp);
985 		}
986 		if (tTd(21, 4))
987 		{
988 			printf("rewritten as:");
989 			printav(pvp);
990 		}
991 	}
992 
993 	if (OpMode == MD_TEST || tTd(21, 2))
994 	{
995 		printf("rewrite: ruleset %2d returns:", ruleset);
996 		printav(pvp);
997 	}
998 }
999 /*
1000 **  BUILDADDR -- build address from token vector.
1001 **
1002 **	Parameters:
1003 **		tv -- token vector.
1004 **		a -- pointer to address descriptor to fill.
1005 **			If NULL, one will be allocated.
1006 **
1007 **	Returns:
1008 **		NULL if there was an error.
1009 **		'a' otherwise.
1010 **
1011 **	Side Effects:
1012 **		fills in 'a'
1013 */
1014 
1015 struct errcodes
1016 {
1017 	char	*ec_name;		/* name of error code */
1018 	int	ec_code;		/* numeric code */
1019 } ErrorCodes[] =
1020 {
1021 	"usage",	EX_USAGE,
1022 	"nouser",	EX_NOUSER,
1023 	"nohost",	EX_NOHOST,
1024 	"unavailable",	EX_UNAVAILABLE,
1025 	"software",	EX_SOFTWARE,
1026 	"tempfail",	EX_TEMPFAIL,
1027 	"protocol",	EX_PROTOCOL,
1028 #ifdef EX_CONFIG
1029 	"config",	EX_CONFIG,
1030 #endif
1031 	NULL,		EX_UNAVAILABLE,
1032 };
1033 
1034 ADDRESS *
1035 buildaddr(tv, a)
1036 	register char **tv;
1037 	register ADDRESS *a;
1038 {
1039 	struct mailer **mp;
1040 	register struct mailer *m;
1041 	static char buf[MAXNAME];
1042 
1043 	if (a == NULL)
1044 		a = (ADDRESS *) xalloc(sizeof *a);
1045 	bzero((char *) a, sizeof *a);
1046 
1047 	/* figure out what net/mailer to use */
1048 	if (**tv != CANONNET)
1049 	{
1050 		syserr("buildaddr: no net");
1051 		return (NULL);
1052 	}
1053 	tv++;
1054 	if (!strcasecmp(*tv, "error"))
1055 	{
1056 		if (**++tv == CANONHOST)
1057 		{
1058 			register struct errcodes *ep;
1059 
1060 			if (isdigit(**++tv))
1061 			{
1062 				setstat(atoi(*tv));
1063 			}
1064 			else
1065 			{
1066 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1067 					if (strcasecmp(ep->ec_name, *tv) == 0)
1068 						break;
1069 				setstat(ep->ec_code);
1070 			}
1071 			tv++;
1072 		}
1073 		if (**tv != CANONUSER)
1074 			syserr("buildaddr: error: no user");
1075 		buf[0] = '\0';
1076 		while (*++tv != NULL)
1077 		{
1078 			if (buf[0] != '\0')
1079 				(void) strcat(buf, " ");
1080 			(void) strcat(buf, *tv);
1081 		}
1082 		usrerr(buf);
1083 		return (NULL);
1084 	}
1085 
1086 	for (mp = Mailer; (m = *mp++) != NULL; )
1087 	{
1088 		if (!strcasecmp(m->m_name, *tv))
1089 			break;
1090 	}
1091 	if (m == NULL)
1092 	{
1093 		syserr("buildaddr: unknown mailer %s", *tv);
1094 		return (NULL);
1095 	}
1096 	a->q_mailer = m;
1097 
1098 	/* figure out what host (if any) */
1099 	tv++;
1100 	if (!bitnset(M_LOCAL, m->m_flags))
1101 	{
1102 		if (**tv++ != CANONHOST)
1103 		{
1104 			syserr("buildaddr: no host");
1105 			return (NULL);
1106 		}
1107 		buf[0] = '\0';
1108 		while (*tv != NULL && **tv != CANONUSER)
1109 			(void) strcat(buf, *tv++);
1110 		a->q_host = newstr(buf);
1111 	}
1112 	else
1113 		a->q_host = NULL;
1114 
1115 	/* figure out the user */
1116 	if (*tv == NULL || **tv != CANONUSER)
1117 	{
1118 		syserr("buildaddr: no user");
1119 		return (NULL);
1120 	}
1121 
1122 	if (m == LocalMailer && tv[1] != NULL && strcmp(tv[1], "@") == 0)
1123 	{
1124 		tv++;
1125 		a->q_flags |= QNOTREMOTE;
1126 	}
1127 	tv++;
1128 
1129 	/* do special mapping for local mailer */
1130 	if (m == LocalMailer && *tv != NULL)
1131 	{
1132 		register char *p = *tv;
1133 
1134 		if (*p == '"')
1135 			p++;
1136 		if (*p == '|')
1137 			a->q_mailer = m = ProgMailer;
1138 		else if (*p == '/')
1139 			a->q_mailer = m = FileMailer;
1140 		else if (*p == ':')
1141 		{
1142 			/* may be :include: */
1143 			cataddr(tv, buf, sizeof buf);
1144 			p = buf;
1145 			if (*p == '"')
1146 				p++;
1147 			p[9] = '\0';
1148 			if (strcasecmp(p, ":include:") == 0)
1149 				a->q_mailer = m = InclMailer;
1150 		}
1151 	}
1152 
1153 	/* rewrite according recipient mailer rewriting rules */
1154 	rewrite(tv, 2);
1155 	if (m->m_r_rwset > 0)
1156 		rewrite(tv, m->m_r_rwset);
1157 	rewrite(tv, 4);
1158 
1159 	/* save the result for the command line/RCPT argument */
1160 	cataddr(tv, buf, sizeof buf);
1161 	a->q_user = buf;
1162 
1163 	return (a);
1164 }
1165 /*
1166 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1167 **
1168 **	Parameters:
1169 **		pvp -- parameter vector to rebuild.
1170 **		buf -- buffer to build the string into.
1171 **		sz -- size of buf.
1172 **
1173 **	Returns:
1174 **		none.
1175 **
1176 **	Side Effects:
1177 **		Destroys buf.
1178 */
1179 
1180 cataddr(pvp, buf, sz)
1181 	char **pvp;
1182 	char *buf;
1183 	register int sz;
1184 {
1185 	bool oatomtok = FALSE;
1186 	bool natomtok = FALSE;
1187 	register int i;
1188 	register char *p;
1189 
1190 	if (pvp == NULL)
1191 	{
1192 		(void) strcpy(buf, "");
1193 		return;
1194 	}
1195 	p = buf;
1196 	sz -= 2;
1197 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1198 	{
1199 		natomtok = (toktype(**pvp) == ATM);
1200 		if (oatomtok && natomtok)
1201 			*p++ = SpaceSub;
1202 		(void) strcpy(p, *pvp);
1203 		oatomtok = natomtok;
1204 		p += i;
1205 		sz -= i + 1;
1206 		pvp++;
1207 	}
1208 	*p = '\0';
1209 }
1210 /*
1211 **  SAMEADDR -- Determine if two addresses are the same
1212 **
1213 **	This is not just a straight comparison -- if the mailer doesn't
1214 **	care about the host we just ignore it, etc.
1215 **
1216 **	Parameters:
1217 **		a, b -- pointers to the internal forms to compare.
1218 **
1219 **	Returns:
1220 **		TRUE -- they represent the same mailbox.
1221 **		FALSE -- they don't.
1222 **
1223 **	Side Effects:
1224 **		none.
1225 */
1226 
1227 bool
1228 sameaddr(a, b)
1229 	register ADDRESS *a;
1230 	register ADDRESS *b;
1231 {
1232 	/* if they don't have the same mailer, forget it */
1233 	if (a->q_mailer != b->q_mailer)
1234 		return (FALSE);
1235 
1236 	/* if the user isn't the same, we can drop out */
1237 	if (strcmp(a->q_user, b->q_user) != 0)
1238 		return (FALSE);
1239 
1240 	/* if the mailer ignores hosts, we have succeeded! */
1241 	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
1242 		return (TRUE);
1243 
1244 	/* otherwise compare hosts (but be careful for NULL ptrs) */
1245 	if (a->q_host == NULL || b->q_host == NULL)
1246 		return (FALSE);
1247 	if (strcmp(a->q_host, b->q_host) != 0)
1248 		return (FALSE);
1249 
1250 	return (TRUE);
1251 }
1252 /*
1253 **  PRINTADDR -- print address (for debugging)
1254 **
1255 **	Parameters:
1256 **		a -- the address to print
1257 **		follow -- follow the q_next chain.
1258 **
1259 **	Returns:
1260 **		none.
1261 **
1262 **	Side Effects:
1263 **		none.
1264 */
1265 
1266 printaddr(a, follow)
1267 	register ADDRESS *a;
1268 	bool follow;
1269 {
1270 	bool first = TRUE;
1271 
1272 	while (a != NULL)
1273 	{
1274 		first = FALSE;
1275 		printf("%x=", a);
1276 		(void) fflush(stdout);
1277 		printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n",
1278 		       a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name,
1279 		       a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>");
1280 		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
1281 		       a->q_alias);
1282 		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
1283 		       a->q_fullname);
1284 
1285 		if (!follow)
1286 			return;
1287 		a = a->q_next;
1288 	}
1289 	if (first)
1290 		printf("[NULL]\n");
1291 }
1292 
1293 /*
1294 **  REMOTENAME -- return the name relative to the current mailer
1295 **
1296 **	Parameters:
1297 **		name -- the name to translate.
1298 **		m -- the mailer that we want to do rewriting relative
1299 **			to.
1300 **		senderaddress -- if set, uses the sender rewriting rules
1301 **			rather than the recipient rewriting rules.
1302 **		canonical -- if set, strip out any comment information,
1303 **			etc.
1304 **
1305 **	Returns:
1306 **		the text string representing this address relative to
1307 **			the receiving mailer.
1308 **
1309 **	Side Effects:
1310 **		none.
1311 **
1312 **	Warnings:
1313 **		The text string returned is tucked away locally;
1314 **			copy it if you intend to save it.
1315 */
1316 
1317 char *
1318 remotename(name, m, senderaddress, canonical, e)
1319 	char *name;
1320 	struct mailer *m;
1321 	bool senderaddress;
1322 	bool canonical;
1323 	register ENVELOPE *e;
1324 {
1325 	register char **pvp;
1326 	char *fancy;
1327 	extern char *macvalue();
1328 	char *oldg = macvalue('g', e);
1329 	static char buf[MAXNAME];
1330 	char lbuf[MAXNAME];
1331 	char pvpbuf[PSBUFSIZE];
1332 	extern char **prescan();
1333 	extern char *crackaddr();
1334 
1335 	if (tTd(12, 1))
1336 		printf("remotename(%s)\n", name);
1337 
1338 	/* don't do anything if we are tagging it as special */
1339 	if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
1340 		return (name);
1341 
1342 	/*
1343 	**  Do a heuristic crack of this name to extract any comment info.
1344 	**	This will leave the name as a comment and a $g macro.
1345 	*/
1346 
1347 	if (canonical)
1348 		fancy = "\001g";
1349 	else
1350 		fancy = crackaddr(name);
1351 
1352 	/*
1353 	**  Turn the name into canonical form.
1354 	**	Normally this will be RFC 822 style, i.e., "user@domain".
1355 	**	If this only resolves to "user", and the "C" flag is
1356 	**	specified in the sending mailer, then the sender's
1357 	**	domain will be appended.
1358 	*/
1359 
1360 	pvp = prescan(name, '\0', pvpbuf);
1361 	if (pvp == NULL)
1362 		return (name);
1363 	rewrite(pvp, 3);
1364 	if (e->e_fromdomain != NULL)
1365 	{
1366 		/* append from domain to this address */
1367 		register char **pxp = pvp;
1368 
1369 		/* see if there is an "@domain" in the current name */
1370 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1371 			pxp++;
1372 		if (*pxp == NULL)
1373 		{
1374 			/* no.... append the "@domain" from the sender */
1375 			register char **qxq = e->e_fromdomain;
1376 
1377 			while ((*pxp++ = *qxq++) != NULL)
1378 				continue;
1379 			rewrite(pvp, 3);
1380 		}
1381 	}
1382 
1383 	/*
1384 	**  Do more specific rewriting.
1385 	**	Rewrite using ruleset 1 or 2 depending on whether this is
1386 	**		a sender address or not.
1387 	**	Then run it through any receiving-mailer-specific rulesets.
1388 	*/
1389 
1390 	if (senderaddress)
1391 	{
1392 		rewrite(pvp, 1);
1393 		if (m->m_s_rwset > 0)
1394 			rewrite(pvp, m->m_s_rwset);
1395 	}
1396 	else
1397 	{
1398 		rewrite(pvp, 2);
1399 		if (m->m_r_rwset > 0)
1400 			rewrite(pvp, m->m_r_rwset);
1401 	}
1402 
1403 	/*
1404 	**  Do any final sanitation the address may require.
1405 	**	This will normally be used to turn internal forms
1406 	**	(e.g., user@host.LOCAL) into external form.  This
1407 	**	may be used as a default to the above rules.
1408 	*/
1409 
1410 	rewrite(pvp, 4);
1411 
1412 	/*
1413 	**  Now restore the comment information we had at the beginning.
1414 	*/
1415 
1416 	cataddr(pvp, lbuf, sizeof lbuf);
1417 	define('g', lbuf, e);
1418 	expand(fancy, buf, &buf[sizeof buf - 1], e);
1419 	define('g', oldg, e);
1420 
1421 	if (tTd(12, 1))
1422 		printf("remotename => `%s'\n", buf);
1423 	return (buf);
1424 }
1425 /*
1426 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
1427 **
1428 **	Parameters:
1429 **		a -- the address to map (but just the user name part).
1430 **		sendq -- the sendq in which to install any replacement
1431 **			addresses.
1432 **
1433 **	Returns:
1434 **		none.
1435 */
1436 
1437 maplocaluser(a, sendq, e)
1438 	register ADDRESS *a;
1439 	ADDRESS **sendq;
1440 	ENVELOPE *e;
1441 {
1442 	register char **pvp;
1443 	register ADDRESS *a1 = NULL;
1444 	char pvpbuf[PSBUFSIZE];
1445 
1446 	if (tTd(29, 1))
1447 	{
1448 		printf("maplocaluser: ");
1449 		printaddr(a, FALSE);
1450 	}
1451 	pvp = prescan(a->q_user, '\0', pvpbuf);
1452 	if (pvp == NULL)
1453 		return;
1454 
1455 	rewrite(pvp, 5);
1456 	if (pvp[0] == NULL || pvp[0][0] != CANONNET)
1457 		return;
1458 
1459 	/* if non-null, mailer destination specified -- has it changed? */
1460 	a1 = buildaddr(pvp, NULL);
1461 	if (a1 == NULL || sameaddr(a, a1))
1462 		return;
1463 
1464 	/* mark old address as dead; insert new address */
1465 	a->q_flags |= QDONTSEND;
1466 	a1->q_alias = a;
1467 	allocaddr(a1, 1, NULL);
1468 	(void) recipient(a1, sendq, e);
1469 }
1470