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