1 # include "sendmail.h"
2 
3 SCCSID(@(#)parseaddr.c	4.15		04/28/85);
4 
5 /*
6 **  PARSEADDR -- Parse an address
7 **
8 **	Parses an address and breaks it up into three parts: a
9 **	net to transmit the message on, the host to transmit it
10 **	to, and a user on that host.  These are loaded into an
11 **	ADDRESS header with the values squirreled away if necessary.
12 **	The "user" part may not be a real user; the process may
13 **	just reoccur on that machine.  For example, on a machine
14 **	with an arpanet connection, the address
15 **		csvax.bill@berkeley
16 **	will break up to a "user" of 'csvax.bill' and a host
17 **	of 'berkeley' -- to be transmitted over the arpanet.
18 **
19 **	Parameters:
20 **		addr -- the address to parse.
21 **		a -- a pointer to the address descriptor buffer.
22 **			If NULL, a header will be created.
23 **		copyf -- determines what shall be copied:
24 **			-1 -- don't copy anything.  The printname
25 **				(q_paddr) is just addr, and the
26 **				user & host are allocated internally
27 **				to parse.
28 **			0 -- copy out the parsed user & host, but
29 **				don't copy the printname.
30 **			+1 -- copy everything.
31 **		delim -- the character to terminate the address, passed
32 **			to prescan.
33 **
34 **	Returns:
35 **		A pointer to the address descriptor header (`a' if
36 **			`a' is non-NULL).
37 **		NULL on error.
38 **
39 **	Side Effects:
40 **		none
41 */
42 
43 /* following delimiters are inherent to the internal algorithms */
44 # define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
45 
46 ADDRESS *
47 parseaddr(addr, a, copyf, delim)
48 	char *addr;
49 	register ADDRESS *a;
50 	int copyf;
51 	char delim;
52 {
53 	register char **pvp;
54 	register struct mailer *m;
55 	char pvpbuf[PSBUFSIZE];
56 	extern char **prescan();
57 	extern ADDRESS *buildaddr();
58 
59 	/*
60 	**  Initialize and prescan address.
61 	*/
62 
63 	CurEnv->e_to = addr;
64 # ifdef DEBUG
65 	if (tTd(20, 1))
66 		printf("\n--parseaddr(%s)\n", addr);
67 # endif DEBUG
68 
69 	pvp = prescan(addr, delim, pvpbuf);
70 	if (pvp == NULL)
71 		return (NULL);
72 
73 	/*
74 	**  Apply rewriting rules.
75 	**	Ruleset 0 does basic parsing.  It must resolve.
76 	*/
77 
78 	rewrite(pvp, 3);
79 	rewrite(pvp, 0);
80 
81 	/*
82 	**  See if we resolved to a real mailer.
83 	*/
84 
85 	if (pvp[0][0] != CANONNET)
86 	{
87 		setstat(EX_USAGE);
88 		usrerr("cannot resolve name");
89 		return (NULL);
90 	}
91 
92 	/*
93 	**  Build canonical address from pvp.
94 	*/
95 
96 	a = buildaddr(pvp, a);
97 	if (a == NULL)
98 		return (NULL);
99 	m = a->q_mailer;
100 
101 	/*
102 	**  Make local copies of the host & user and then
103 	**  transport them out.
104 	*/
105 
106 	if (copyf > 0)
107 	{
108 		extern char *DelimChar;
109 		char savec = *DelimChar;
110 
111 		*DelimChar = '\0';
112 		a->q_paddr = newstr(addr);
113 		*DelimChar = savec;
114 	}
115 	else
116 		a->q_paddr = addr;
117 	if (copyf >= 0)
118 	{
119 		if (a->q_host != NULL)
120 			a->q_host = newstr(a->q_host);
121 		else
122 			a->q_host = "";
123 		if (a->q_user != a->q_paddr)
124 			a->q_user = newstr(a->q_user);
125 	}
126 
127 	/*
128 	**  Convert host name to lower case if requested.
129 	**	User name will be done later.
130 	*/
131 
132 	if (!bitnset(M_HST_UPPER, m->m_flags))
133 		makelower(a->q_host);
134 
135 	/*
136 	**  Compute return value.
137 	*/
138 
139 # ifdef DEBUG
140 	if (tTd(20, 1))
141 	{
142 		printf("parseaddr-->");
143 		printaddr(a, FALSE);
144 	}
145 # endif DEBUG
146 
147 	return (a);
148 }
149 /*
150 **  LOWERADDR -- map UPPER->lower case on addresses as requested.
151 **
152 **	Parameters:
153 **		a -- address to be mapped.
154 **
155 **	Returns:
156 **		none.
157 **
158 **	Side Effects:
159 **		none.
160 */
161 
162 loweraddr(a)
163 	register ADDRESS *a;
164 {
165 	register MAILER *m = a->q_mailer;
166 
167 	if (!bitnset(M_USR_UPPER, m->m_flags))
168 		makelower(a->q_user);
169 }
170 /*
171 **  PRESCAN -- Prescan name and make it canonical
172 **
173 **	Scans a name and turns it into a set of tokens.  This process
174 **	deletes blanks and comments (in parentheses).
175 **
176 **	This routine knows about quoted strings and angle brackets.
177 **
178 **	There are certain subtleties to this routine.  The one that
179 **	comes to mind now is that backslashes on the ends of names
180 **	are silently stripped off; this is intentional.  The problem
181 **	is that some versions of sndmsg (like at LBL) set the kill
182 **	character to something other than @ when reading addresses;
183 **	so people type "csvax.eric\@berkeley" -- which screws up the
184 **	berknet mailer.
185 **
186 **	Parameters:
187 **		addr -- the name to chomp.
188 **		delim -- the delimiter for the address, normally
189 **			'\0' or ','; \0 is accepted in any case.
190 **			If '\t' then we are reading the .cf file.
191 **		pvpbuf -- place to put the saved text -- note that
192 **			the pointers are static.
193 **
194 **	Returns:
195 **		A pointer to a vector of tokens.
196 **		NULL on error.
197 **
198 **	Side Effects:
199 **		none.
200 */
201 
202 /* states and character types */
203 # define OPR		0	/* operator */
204 # define ATM		1	/* atom */
205 # define QST		2	/* in quoted string */
206 # define SPC		3	/* chewing up spaces */
207 # define ONE		4	/* pick up one character */
208 
209 # define NSTATES	5	/* number of states */
210 # define TYPE		017	/* mask to select state type */
211 
212 /* meta bits for table */
213 # define M		020	/* meta character; don't pass through */
214 # define B		040	/* cause a break */
215 # define MB		M|B	/* meta-break */
216 
217 static short StateTab[NSTATES][NSTATES] =
218 {
219    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
220 	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
221 	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
222 	/*QST*/		QST,	QST,	OPR,	QST,	QST,
223 	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
224 	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
225 };
226 
227 # define NOCHAR		-1	/* signal nothing in lookahead token */
228 
229 char	*DelimChar;		/* set to point to the delimiter */
230 
231 char **
232 prescan(addr, delim, pvpbuf)
233 	char *addr;
234 	char delim;
235 	char pvpbuf[];
236 {
237 	register char *p;
238 	register char *q;
239 	register int c;
240 	char **avp;
241 	bool bslashmode;
242 	int cmntcnt;
243 	int anglecnt;
244 	char *tok;
245 	int state;
246 	int newstate;
247 	static char *av[MAXATOM+1];
248 	extern int errno;
249 
250 	/* make sure error messages don't have garbage on them */
251 	errno = 0;
252 
253 	q = pvpbuf;
254 	bslashmode = FALSE;
255 	cmntcnt = 0;
256 	anglecnt = 0;
257 	avp = av;
258 	state = OPR;
259 	c = NOCHAR;
260 	p = addr;
261 # ifdef DEBUG
262 	if (tTd(22, 45))
263 	{
264 		printf("prescan: ");
265 		xputs(p);
266 		putchar('\n');
267 	}
268 # endif DEBUG
269 
270 	do
271 	{
272 		/* read a token */
273 		tok = q;
274 		for (;;)
275 		{
276 			/* store away any old lookahead character */
277 			if (c != NOCHAR)
278 			{
279 				/* see if there is room */
280 				if (q >= &pvpbuf[PSBUFSIZE - 5])
281 				{
282 					usrerr("Address too long");
283 					DelimChar = p;
284 					return (NULL);
285 				}
286 
287 				/* squirrel it away */
288 				*q++ = c;
289 			}
290 
291 			/* read a new input character */
292 			c = *p++;
293 			if (c == '\0')
294 				break;
295 			c &= ~0200;
296 
297 # ifdef DEBUG
298 			if (tTd(22, 101))
299 				printf("c=%c, s=%d; ", c, state);
300 # endif DEBUG
301 
302 			/* chew up special characters */
303 			*q = '\0';
304 			if (bslashmode)
305 			{
306 				c |= 0200;
307 				bslashmode = FALSE;
308 			}
309 			else if (c == '\\')
310 			{
311 				bslashmode = TRUE;
312 				c = NOCHAR;
313 			}
314 			else if (state == QST)
315 			{
316 				/* do nothing, just avoid next clauses */
317 			}
318 			else if (c == '(')
319 			{
320 				cmntcnt++;
321 				c = NOCHAR;
322 			}
323 			else if (c == ')')
324 			{
325 				if (cmntcnt <= 0)
326 				{
327 					usrerr("Unbalanced ')'");
328 					DelimChar = p;
329 					return (NULL);
330 				}
331 				else
332 					cmntcnt--;
333 			}
334 			else if (cmntcnt > 0)
335 				c = NOCHAR;
336 			else if (c == '<')
337 				anglecnt++;
338 			else if (c == '>')
339 			{
340 				if (anglecnt <= 0)
341 				{
342 					usrerr("Unbalanced '>'");
343 					DelimChar = p;
344 					return (NULL);
345 				}
346 				anglecnt--;
347 			}
348 			else if (delim == ' ' && isspace(c))
349 				c = ' ';
350 
351 			if (c == NOCHAR)
352 				continue;
353 
354 			/* see if this is end of input */
355 			if (c == delim && anglecnt <= 0 && state != QST)
356 				break;
357 
358 			newstate = StateTab[state][toktype(c)];
359 # ifdef DEBUG
360 			if (tTd(22, 101))
361 				printf("ns=%02o\n", newstate);
362 # endif DEBUG
363 			state = newstate & TYPE;
364 			if (bitset(M, newstate))
365 				c = NOCHAR;
366 			if (bitset(B, newstate))
367 				break;
368 		}
369 
370 		/* new token */
371 		if (tok != q)
372 		{
373 			*q++ = '\0';
374 # ifdef DEBUG
375 			if (tTd(22, 36))
376 			{
377 				printf("tok=");
378 				xputs(tok);
379 				putchar('\n');
380 			}
381 # endif DEBUG
382 			if (avp >= &av[MAXATOM])
383 			{
384 				syserr("prescan: too many tokens");
385 				DelimChar = p;
386 				return (NULL);
387 			}
388 			*avp++ = tok;
389 		}
390 	} while (c != '\0' && (c != delim || anglecnt > 0));
391 	*avp = NULL;
392 	DelimChar = --p;
393 	if (cmntcnt > 0)
394 		usrerr("Unbalanced '('");
395 	else if (anglecnt > 0)
396 		usrerr("Unbalanced '<'");
397 	else if (state == QST)
398 		usrerr("Unbalanced '\"'");
399 	else if (av[0] != NULL)
400 		return (av);
401 	return (NULL);
402 }
403 /*
404 **  TOKTYPE -- return token type
405 **
406 **	Parameters:
407 **		c -- the character in question.
408 **
409 **	Returns:
410 **		Its type.
411 **
412 **	Side Effects:
413 **		none.
414 */
415 
416 toktype(c)
417 	register char c;
418 {
419 	static char buf[50];
420 	static bool firstime = TRUE;
421 
422 	if (firstime)
423 	{
424 		firstime = FALSE;
425 		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
426 		(void) strcat(buf, DELIMCHARS);
427 	}
428 	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
429 		return (ONE);
430 	if (c == '"')
431 		return (QST);
432 	if (!isascii(c))
433 		return (ATM);
434 	if (isspace(c) || c == ')')
435 		return (SPC);
436 	if (iscntrl(c) || index(buf, c) != NULL)
437 		return (OPR);
438 	return (ATM);
439 }
440 /*
441 **  REWRITE -- apply rewrite rules to token vector.
442 **
443 **	This routine is an ordered production system.  Each rewrite
444 **	rule has a LHS (called the pattern) and a RHS (called the
445 **	rewrite); 'rwr' points the the current rewrite rule.
446 **
447 **	For each rewrite rule, 'avp' points the address vector we
448 **	are trying to match against, and 'pvp' points to the pattern.
449 **	If pvp points to a special match value (MATCHZANY, MATCHANY,
450 **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
451 **	matched is saved away in the match vector (pointed to by 'mvp').
452 **
453 **	When a match between avp & pvp does not match, we try to
454 **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
455 **	we must also back out the match in mvp.  If we reach a
456 **	MATCHANY or MATCHZANY we just extend the match and start
457 **	over again.
458 **
459 **	When we finally match, we rewrite the address vector
460 **	and try over again.
461 **
462 **	Parameters:
463 **		pvp -- pointer to token vector.
464 **
465 **	Returns:
466 **		none.
467 **
468 **	Side Effects:
469 **		pvp is modified.
470 */
471 
472 struct match
473 {
474 	char	**first;	/* first token matched */
475 	char	**last;		/* last token matched */
476 };
477 
478 # define MAXMATCH	9	/* max params per rewrite */
479 
480 
481 rewrite(pvp, ruleset)
482 	char **pvp;
483 	int ruleset;
484 {
485 	register char *ap;		/* address pointer */
486 	register char *rp;		/* rewrite pointer */
487 	register char **avp;		/* address vector pointer */
488 	register char **rvp;		/* rewrite vector pointer */
489 	register struct match *mlp;	/* cur ptr into mlist */
490 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
491 	struct match mlist[MAXMATCH];	/* stores match on LHS */
492 	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
493 	extern bool sameword();
494 
495 	if (OpMode == MD_TEST || tTd(21, 2))
496 	{
497 		printf("rewrite: ruleset %2d   input:", ruleset);
498 		printav(pvp);
499 	}
500 	if (pvp == NULL)
501 		return;
502 
503 	/*
504 	**  Run through the list of rewrite rules, applying
505 	**	any that match.
506 	*/
507 
508 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
509 	{
510 # ifdef DEBUG
511 		if (tTd(21, 12))
512 		{
513 			printf("-----trying rule:");
514 			printav(rwr->r_lhs);
515 		}
516 # endif DEBUG
517 
518 		/* try to match on this rule */
519 		mlp = mlist;
520 		rvp = rwr->r_lhs;
521 		avp = pvp;
522 		while ((ap = *avp) != NULL || *rvp != NULL)
523 		{
524 			rp = *rvp;
525 # ifdef DEBUG
526 			if (tTd(21, 35))
527 			{
528 				printf("ap=");
529 				xputs(ap);
530 				printf(", rp=");
531 				xputs(rp);
532 				printf("\n");
533 			}
534 # endif DEBUG
535 			if (rp == NULL)
536 			{
537 				/* end-of-pattern before end-of-address */
538 				goto backup;
539 			}
540 			if (ap == NULL && *rp != MATCHZANY)
541 			{
542 				/* end-of-input */
543 				break;
544 			}
545 
546 			switch (*rp)
547 			{
548 				register STAB *s;
549 
550 			  case MATCHCLASS:
551 			  case MATCHNCLASS:
552 				/* match any token in (not in) a class */
553 				s = stab(ap, ST_CLASS, ST_FIND);
554 				if (s == NULL || !bitnset(rp[1], s->s_class))
555 				{
556 					if (*rp == MATCHCLASS)
557 						goto backup;
558 				}
559 				else if (*rp == MATCHNCLASS)
560 					goto backup;
561 
562 				/* explicit fall-through */
563 
564 			  case MATCHONE:
565 			  case MATCHANY:
566 				/* match exactly one token */
567 				mlp->first = avp;
568 				mlp->last = avp++;
569 				mlp++;
570 				break;
571 
572 			  case MATCHZANY:
573 				/* match zero or more tokens */
574 				mlp->first = avp;
575 				mlp->last = avp - 1;
576 				mlp++;
577 				break;
578 
579 			  default:
580 				/* must have exact match */
581 				if (!sameword(rp, ap))
582 					goto backup;
583 				avp++;
584 				break;
585 			}
586 
587 			/* successful match on this token */
588 			rvp++;
589 			continue;
590 
591 		  backup:
592 			/* match failed -- back up */
593 			while (--rvp >= rwr->r_lhs)
594 			{
595 				rp = *rvp;
596 				if (*rp == MATCHANY || *rp == MATCHZANY)
597 				{
598 					/* extend binding and continue */
599 					avp = ++mlp[-1].last;
600 					avp++;
601 					rvp++;
602 					break;
603 				}
604 				avp--;
605 				if (*rp == MATCHONE || *rp == MATCHCLASS ||
606 				    *rp == MATCHNCLASS)
607 				{
608 					/* back out binding */
609 					mlp--;
610 				}
611 			}
612 
613 			if (rvp < rwr->r_lhs)
614 			{
615 				/* total failure to match */
616 				break;
617 			}
618 		}
619 
620 		/*
621 		**  See if we successfully matched
622 		*/
623 
624 		if (rvp < rwr->r_lhs || *rvp != NULL)
625 		{
626 # ifdef DEBUG
627 			if (tTd(21, 10))
628 				printf("----- rule fails\n");
629 # endif DEBUG
630 			rwr = rwr->r_next;
631 			continue;
632 		}
633 
634 		rvp = rwr->r_rhs;
635 # ifdef DEBUG
636 		if (tTd(21, 12))
637 		{
638 			printf("-----rule matches:");
639 			printav(rvp);
640 		}
641 # endif DEBUG
642 
643 		rp = *rvp;
644 		if (*rp == CANONUSER)
645 		{
646 			rvp++;
647 			rwr = rwr->r_next;
648 		}
649 		else if (*rp == CANONHOST)
650 		{
651 			rvp++;
652 			rwr = NULL;
653 		}
654 		else if (*rp == CANONNET)
655 			rwr = NULL;
656 
657 		/* substitute */
658 		for (avp = npvp; *rvp != NULL; rvp++)
659 		{
660 			register struct match *m;
661 			register char **pp;
662 
663 			rp = *rvp;
664 			if (*rp == MATCHREPL)
665 			{
666 				/* substitute from LHS */
667 				m = &mlist[rp[1] - '1'];
668 				if (m >= mlp)
669 				{
670 					syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
671 					return;
672 				}
673 # ifdef DEBUG
674 				if (tTd(21, 15))
675 				{
676 					printf("$%c:", rp[1]);
677 					pp = m->first;
678 					while (pp <= m->last)
679 					{
680 						printf(" %x=\"", *pp);
681 						(void) fflush(stdout);
682 						printf("%s\"", *pp++);
683 					}
684 					printf("\n");
685 				}
686 # endif DEBUG
687 				pp = m->first;
688 				while (pp <= m->last)
689 				{
690 					if (avp >= &npvp[MAXATOM])
691 					{
692 						syserr("rewrite: expansion too long");
693 						return;
694 					}
695 					*avp++ = *pp++;
696 				}
697 			}
698 			else
699 			{
700 				/* vanilla replacement */
701 				if (avp >= &npvp[MAXATOM])
702 				{
703 	toolong:
704 					syserr("rewrite: expansion too long");
705 					return;
706 				}
707 				*avp++ = rp;
708 			}
709 		}
710 		*avp++ = NULL;
711 
712 		/*
713 		**  Check for any hostname lookups.
714 		*/
715 
716 		for (rvp = npvp; *rvp != NULL; rvp++)
717 		{
718 			char **hbrvp;
719 			char **xpvp;
720 			int trsize;
721 			int i;
722 			char *olddelimchar;
723 			char buf[MAXNAME + 1];
724 			char *pvpb1[MAXATOM + 1];
725 			char pvpbuf[PSBUFSIZE];
726 			extern char *DelimChar;
727 
728 			if (**rvp != HOSTBEGIN)
729 				continue;
730 
731 			/*
732 			**  Got a hostname lookup.
733 			**
734 			**	This could be optimized fairly easily.
735 			*/
736 
737 			hbrvp = rvp;
738 
739 			/* extract the match part */
740 			while (*++rvp != NULL && **rvp != HOSTEND)
741 				continue;
742 			if (*rvp != NULL)
743 				*rvp++ = NULL;
744 
745 			/* save the remainder of the input string */
746 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
747 			bcopy((char *) rvp, (char *) pvpb1, trsize);
748 
749 			/* look it up */
750 			cataddr(++hbrvp, buf, sizeof buf);
751 			maphostname(buf, sizeof buf);
752 
753 			/* scan the new host name */
754 			olddelimchar = DelimChar;
755 			xpvp = prescan(buf, '\0', pvpbuf);
756 			DelimChar = olddelimchar;
757 			if (xpvp == NULL)
758 			{
759 				syserr("rewrite: cannot prescan canonical hostname: %s", buf);
760 				return (NULL);
761 			}
762 
763 			/* append it to the token list */
764 			for (avp = --hbrvp; *xpvp != NULL; xpvp++)
765 			{
766 				*avp++ = newstr(*xpvp);
767 				if (avp >= &npvp[MAXATOM])
768 					goto toolong;
769 			}
770 
771 			/* restore the old trailing information */
772 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
773 				if (avp >= &npvp[MAXATOM])
774 					goto toolong;
775 
776 			break;
777 		}
778 
779 		/*
780 		**  Check for subroutine calls.
781 		*/
782 
783 		if (**npvp == CALLSUBR)
784 		{
785 			bcopy((char *) &npvp[2], (char *) pvp,
786 				(int) (avp - npvp - 2) * sizeof *avp);
787 # ifdef DEBUG
788 			if (tTd(21, 3))
789 				printf("-----callsubr %s\n", npvp[1]);
790 # endif DEBUG
791 			rewrite(pvp, atoi(npvp[1]));
792 		}
793 		else
794 		{
795 			bcopy((char *) npvp, (char *) pvp,
796 				(int) (avp - npvp) * sizeof *avp);
797 		}
798 # ifdef DEBUG
799 		if (tTd(21, 4))
800 		{
801 			printf("rewritten as:");
802 			printav(pvp);
803 		}
804 # endif DEBUG
805 	}
806 
807 	if (OpMode == MD_TEST || tTd(21, 2))
808 	{
809 		printf("rewrite: ruleset %2d returns:", ruleset);
810 		printav(pvp);
811 	}
812 }
813 /*
814 **  BUILDADDR -- build address from token vector.
815 **
816 **	Parameters:
817 **		tv -- token vector.
818 **		a -- pointer to address descriptor to fill.
819 **			If NULL, one will be allocated.
820 **
821 **	Returns:
822 **		NULL if there was an error.
823 **		'a' otherwise.
824 **
825 **	Side Effects:
826 **		fills in 'a'
827 */
828 
829 ADDRESS *
830 buildaddr(tv, a)
831 	register char **tv;
832 	register ADDRESS *a;
833 {
834 	static char buf[MAXNAME];
835 	struct mailer **mp;
836 	register struct mailer *m;
837 	extern bool sameword();
838 
839 	if (a == NULL)
840 		a = (ADDRESS *) xalloc(sizeof *a);
841 	bzero((char *) a, sizeof *a);
842 
843 	/* figure out what net/mailer to use */
844 	if (**tv != CANONNET)
845 	{
846 		syserr("buildaddr: no net");
847 		return (NULL);
848 	}
849 	tv++;
850 	if (sameword(*tv, "error"))
851 	{
852 		if (**++tv == CANONHOST)
853 		{
854 			setstat(atoi(*++tv));
855 			tv++;
856 		}
857 		if (**tv != CANONUSER)
858 			syserr("buildaddr: error: no user");
859 		buf[0] = '\0';
860 		while (*++tv != NULL)
861 		{
862 			if (buf[0] != '\0')
863 				(void) strcat(buf, " ");
864 			(void) strcat(buf, *tv);
865 		}
866 		usrerr(buf);
867 		return (NULL);
868 	}
869 	for (mp = Mailer; (m = *mp++) != NULL; )
870 	{
871 		if (sameword(m->m_name, *tv))
872 			break;
873 	}
874 	if (m == NULL)
875 	{
876 		syserr("buildaddr: unknown net %s", *tv);
877 		return (NULL);
878 	}
879 	a->q_mailer = m;
880 
881 	/* figure out what host (if any) */
882 	tv++;
883 	if (!bitnset(M_LOCAL, m->m_flags))
884 	{
885 		if (**tv++ != CANONHOST)
886 		{
887 			syserr("buildaddr: no host");
888 			return (NULL);
889 		}
890 		buf[0] = '\0';
891 		while (*tv != NULL && **tv != CANONUSER)
892 			(void) strcat(buf, *tv++);
893 		a->q_host = newstr(buf);
894 	}
895 	else
896 		a->q_host = NULL;
897 
898 	/* figure out the user */
899 	if (**tv != CANONUSER)
900 	{
901 		syserr("buildaddr: no user");
902 		return (NULL);
903 	}
904 
905 	/* rewrite according recipient mailer rewriting rules */
906 	rewrite(++tv, 2);
907 	if (m->m_r_rwset > 0)
908 		rewrite(tv, m->m_r_rwset);
909 	rewrite(tv, 4);
910 
911 	/* save the result for the command line/RCPT argument */
912 	cataddr(tv, buf, sizeof buf);
913 	a->q_user = buf;
914 
915 	return (a);
916 }
917 /*
918 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
919 **
920 **	Parameters:
921 **		pvp -- parameter vector to rebuild.
922 **		buf -- buffer to build the string into.
923 **		sz -- size of buf.
924 **
925 **	Returns:
926 **		none.
927 **
928 **	Side Effects:
929 **		Destroys buf.
930 */
931 
932 cataddr(pvp, buf, sz)
933 	char **pvp;
934 	char *buf;
935 	register int sz;
936 {
937 	bool oatomtok = FALSE;
938 	bool natomtok = FALSE;
939 	register int i;
940 	register char *p;
941 
942 	if (pvp == NULL)
943 	{
944 		strcpy(buf, "");
945 		return;
946 	}
947 	p = buf;
948 	sz -= 2;
949 	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
950 	{
951 		natomtok = (toktype(**pvp) == ATM);
952 		if (oatomtok && natomtok)
953 			*p++ = SpaceSub;
954 		(void) strcpy(p, *pvp);
955 		oatomtok = natomtok;
956 		p += i;
957 		sz -= i + 1;
958 		pvp++;
959 	}
960 	*p = '\0';
961 }
962 /*
963 **  SAMEADDR -- Determine if two addresses are the same
964 **
965 **	This is not just a straight comparison -- if the mailer doesn't
966 **	care about the host we just ignore it, etc.
967 **
968 **	Parameters:
969 **		a, b -- pointers to the internal forms to compare.
970 **
971 **	Returns:
972 **		TRUE -- they represent the same mailbox.
973 **		FALSE -- they don't.
974 **
975 **	Side Effects:
976 **		none.
977 */
978 
979 bool
980 sameaddr(a, b)
981 	register ADDRESS *a;
982 	register ADDRESS *b;
983 {
984 	/* if they don't have the same mailer, forget it */
985 	if (a->q_mailer != b->q_mailer)
986 		return (FALSE);
987 
988 	/* if the user isn't the same, we can drop out */
989 	if (strcmp(a->q_user, b->q_user) != 0)
990 		return (FALSE);
991 
992 	/* if the mailer ignores hosts, we have succeeded! */
993 	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
994 		return (TRUE);
995 
996 	/* otherwise compare hosts (but be careful for NULL ptrs) */
997 	if (a->q_host == NULL || b->q_host == NULL)
998 		return (FALSE);
999 	if (strcmp(a->q_host, b->q_host) != 0)
1000 		return (FALSE);
1001 
1002 	return (TRUE);
1003 }
1004 /*
1005 **  PRINTADDR -- print address (for debugging)
1006 **
1007 **	Parameters:
1008 **		a -- the address to print
1009 **		follow -- follow the q_next chain.
1010 **
1011 **	Returns:
1012 **		none.
1013 **
1014 **	Side Effects:
1015 **		none.
1016 */
1017 
1018 # ifdef DEBUG
1019 
1020 printaddr(a, follow)
1021 	register ADDRESS *a;
1022 	bool follow;
1023 {
1024 	bool first = TRUE;
1025 
1026 	while (a != NULL)
1027 	{
1028 		first = FALSE;
1029 		printf("%x=", a);
1030 		(void) fflush(stdout);
1031 		printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
1032 		       a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host,
1033 		       a->q_user);
1034 		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
1035 		       a->q_alias);
1036 		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
1037 		       a->q_fullname);
1038 
1039 		if (!follow)
1040 			return;
1041 		a = a->q_next;
1042 	}
1043 	if (first)
1044 		printf("[NULL]\n");
1045 }
1046 
1047 # endif DEBUG
1048 /*
1049 **  REMOTENAME -- return the name relative to the current mailer
1050 **
1051 **	Parameters:
1052 **		name -- the name to translate.
1053 **		m -- the mailer that we want to do rewriting relative
1054 **			to.
1055 **		senderaddress -- if set, uses the sender rewriting rules
1056 **			rather than the recipient rewriting rules.
1057 **		canonical -- if set, strip out any comment information,
1058 **			etc.
1059 **
1060 **	Returns:
1061 **		the text string representing this address relative to
1062 **			the receiving mailer.
1063 **
1064 **	Side Effects:
1065 **		none.
1066 **
1067 **	Warnings:
1068 **		The text string returned is tucked away locally;
1069 **			copy it if you intend to save it.
1070 */
1071 
1072 char *
1073 remotename(name, m, senderaddress, canonical)
1074 	char *name;
1075 	struct mailer *m;
1076 	bool senderaddress;
1077 	bool canonical;
1078 {
1079 	register char **pvp;
1080 	char *fancy;
1081 	register char *p;
1082 	extern char *macvalue();
1083 	char *oldg = macvalue('g', CurEnv);
1084 	static char buf[MAXNAME];
1085 	char lbuf[MAXNAME];
1086 	char pvpbuf[PSBUFSIZE];
1087 	extern char **prescan();
1088 	extern char *crackaddr();
1089 
1090 # ifdef DEBUG
1091 	if (tTd(12, 1))
1092 		printf("remotename(%s)\n", name);
1093 # endif DEBUG
1094 
1095 	/* don't do anything if we are tagging it as special */
1096 	if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
1097 		return (name);
1098 
1099 	/*
1100 	**  Do a heuristic crack of this name to extract any comment info.
1101 	**	This will leave the name as a comment and a $g macro.
1102 	*/
1103 
1104 	if (canonical)
1105 		fancy = "\001g";
1106 	else
1107 		fancy = crackaddr(name);
1108 
1109 	/*
1110 	**  Turn the name into canonical form.
1111 	**	Normally this will be RFC 822 style, i.e., "user@domain".
1112 	**	If this only resolves to "user", and the "C" flag is
1113 	**	specified in the sending mailer, then the sender's
1114 	**	domain will be appended.
1115 	*/
1116 
1117 	pvp = prescan(name, '\0', pvpbuf);
1118 	if (pvp == NULL)
1119 		return (name);
1120 	rewrite(pvp, 3);
1121 	if (CurEnv->e_fromdomain != NULL)
1122 	{
1123 		/* append from domain to this address */
1124 		register char **pxp = pvp;
1125 
1126 		/* see if there is an "@domain" in the current name */
1127 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1128 			pxp++;
1129 		if (*pxp == NULL)
1130 		{
1131 			/* no.... append the "@domain" from the sender */
1132 			register char **qxq = CurEnv->e_fromdomain;
1133 
1134 			while ((*pxp++ = *qxq++) != NULL)
1135 				continue;
1136 			rewrite(pvp, 3);
1137 		}
1138 	}
1139 
1140 	/*
1141 	**  Do more specific rewriting.
1142 	**	Rewrite using ruleset 1 or 2 depending on whether this is
1143 	**		a sender address or not.
1144 	**	Then run it through any receiving-mailer-specific rulesets.
1145 	*/
1146 
1147 	if (senderaddress)
1148 	{
1149 		rewrite(pvp, 1);
1150 		if (m->m_s_rwset > 0)
1151 			rewrite(pvp, m->m_s_rwset);
1152 	}
1153 	else
1154 	{
1155 		rewrite(pvp, 2);
1156 		if (m->m_r_rwset > 0)
1157 			rewrite(pvp, m->m_r_rwset);
1158 	}
1159 
1160 	/*
1161 	**  Do any final sanitation the address may require.
1162 	**	This will normally be used to turn internal forms
1163 	**	(e.g., user@host.LOCAL) into external form.  This
1164 	**	may be used as a default to the above rules.
1165 	*/
1166 
1167 	rewrite(pvp, 4);
1168 
1169 	/*
1170 	**  Now restore the comment information we had at the beginning.
1171 	*/
1172 
1173 	cataddr(pvp, lbuf, sizeof lbuf);
1174 	define('g', lbuf, CurEnv);
1175 	expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
1176 	define('g', oldg, CurEnv);
1177 
1178 # ifdef DEBUG
1179 	if (tTd(12, 1))
1180 		printf("remotename => `%s'\n", buf);
1181 # endif DEBUG
1182 	return (buf);
1183 }
1184