xref: /freebsd/contrib/sendmail/src/parseaddr.c (revision 729362425c09cf6b362366aabc6fb547eee8035a)
1 /*
2  * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13 
14 #include <sendmail.h>
15 
16 SM_RCSID("@(#)$Id: parseaddr.c,v 8.359.2.6 2003/03/27 02:39:53 ca Exp $")
17 
18 static void	allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
19 static int	callsubr __P((char**, int, ENVELOPE *));
20 static char	*map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
21 static ADDRESS	*buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
22 static bool	hasctrlchar __P((register char *, bool, bool));
23 
24 /* replacement for illegal characters in addresses */
25 #define BAD_CHAR_REPLACEMENT	'?'
26 
27 /*
28 **  PARSEADDR -- Parse an address
29 **
30 **	Parses an address and breaks it up into three parts: a
31 **	net to transmit the message on, the host to transmit it
32 **	to, and a user on that host.  These are loaded into an
33 **	ADDRESS header with the values squirreled away if necessary.
34 **	The "user" part may not be a real user; the process may
35 **	just reoccur on that machine.  For example, on a machine
36 **	with an arpanet connection, the address
37 **		csvax.bill@berkeley
38 **	will break up to a "user" of 'csvax.bill' and a host
39 **	of 'berkeley' -- to be transmitted over the arpanet.
40 **
41 **	Parameters:
42 **		addr -- the address to parse.
43 **		a -- a pointer to the address descriptor buffer.
44 **			If NULL, an address will be created.
45 **		flags -- describe detail for parsing.  See RF_ definitions
46 **			in sendmail.h.
47 **		delim -- the character to terminate the address, passed
48 **			to prescan.
49 **		delimptr -- if non-NULL, set to the location of the
50 **			delim character that was found.
51 **		e -- the envelope that will contain this address.
52 **		isrcpt -- true if the address denotes a recipient; false
53 **			indicates a sender.
54 **
55 **	Returns:
56 **		A pointer to the address descriptor header (`a' if
57 **			`a' is non-NULL).
58 **		NULL on error.
59 **
60 **	Side Effects:
61 **		e->e_to = addr
62 */
63 
64 /* following delimiters are inherent to the internal algorithms */
65 #define DELIMCHARS	"()<>,;\r\n"	/* default word delimiters */
66 
67 ADDRESS *
68 parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
69 	char *addr;
70 	register ADDRESS *a;
71 	int flags;
72 	int delim;
73 	char **delimptr;
74 	register ENVELOPE *e;
75 	bool isrcpt;
76 {
77 	char **pvp;
78 	auto char *delimptrbuf;
79 	bool qup;
80 	char pvpbuf[PSBUFSIZE];
81 
82 	/*
83 	**  Initialize and prescan address.
84 	*/
85 
86 	e->e_to = addr;
87 	if (tTd(20, 1))
88 		sm_dprintf("\n--parseaddr(%s)\n", addr);
89 
90 	if (delimptr == NULL)
91 		delimptr = &delimptrbuf;
92 
93 	pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
94 	if (pvp == NULL)
95 	{
96 		if (tTd(20, 1))
97 			sm_dprintf("parseaddr-->NULL\n");
98 		return NULL;
99 	}
100 
101 	if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))
102 	{
103 		if (tTd(20, 1))
104 			sm_dprintf("parseaddr-->bad address\n");
105 		return NULL;
106 	}
107 
108 	/*
109 	**  Save addr if we are going to have to.
110 	**
111 	**	We have to do this early because there is a chance that
112 	**	the map lookups in the rewriting rules could clobber
113 	**	static memory somewhere.
114 	*/
115 
116 	if (bitset(RF_COPYPADDR, flags) && addr != NULL)
117 	{
118 		char savec = **delimptr;
119 
120 		if (savec != '\0')
121 			**delimptr = '\0';
122 		e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);
123 		if (savec != '\0')
124 			**delimptr = savec;
125 	}
126 
127 	/*
128 	**  Apply rewriting rules.
129 	**	Ruleset 0 does basic parsing.  It must resolve.
130 	*/
131 
132 	qup = false;
133 	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
134 		qup = true;
135 	if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)
136 		qup = true;
137 
138 	/*
139 	**  Build canonical address from pvp.
140 	*/
141 
142 	a = buildaddr(pvp, a, flags, e);
143 
144 	if (hasctrlchar(a->q_user, isrcpt, true))
145 	{
146 		if (tTd(20, 1))
147 			sm_dprintf("parseaddr-->bad q_user\n");
148 
149 		/*
150 		**  Just mark the address as bad so DSNs work.
151 		**  hasctrlchar() has to make sure that the address
152 		**  has been sanitized, e.g., shortened.
153 		*/
154 
155 		a->q_state = QS_BADADDR;
156 	}
157 
158 	/*
159 	**  Make local copies of the host & user and then
160 	**  transport them out.
161 	*/
162 
163 	allocaddr(a, flags, addr, e);
164 	if (QS_IS_BADADDR(a->q_state))
165 	{
166 		/* weed out bad characters in the printable address too */
167 		(void) hasctrlchar(a->q_paddr, isrcpt, false);
168 		return a;
169 	}
170 
171 	/*
172 	**  Select a queue directory for recipient addresses.
173 	**	This is done here and in split_across_queue_groups(),
174 	**	but the latter applies to addresses after aliasing,
175 	**	and only if splitting is done.
176 	*/
177 
178 	if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&
179 	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags) &&
180 	    OpMode != MD_INITALIAS)
181 	{
182 		int r;
183 
184 		/* call ruleset which should return a queue group name */
185 		r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,
186 			  sizeof(pvpbuf));
187 		if (r == EX_OK &&
188 		    pvp != NULL && pvp[0] != NULL &&
189 		    (pvp[0][0] & 0377) == CANONNET &&
190 		    pvp[1] != NULL && pvp[1][0] != '\0')
191 		{
192 			r = name2qid(pvp[1]);
193 			if (r == NOQGRP && LogLevel > 10)
194 				sm_syslog(LOG_INFO, NOQID,
195 					"can't find queue group name %s, selection ignored",
196 					pvp[1]);
197 			if (tTd(20, 4) && r != NOQGRP)
198 				sm_syslog(LOG_INFO, NOQID,
199 					"queue group name %s -> %d",
200 					pvp[1], r);
201 			a->q_qgrp = r == NOQGRP ? ENVQGRP : r;
202 		}
203 	}
204 
205 	/*
206 	**  If there was a parsing failure, mark it for queueing.
207 	*/
208 
209 	if (qup && OpMode != MD_INITALIAS)
210 	{
211 		char *msg = "Transient parse error -- message queued for future delivery";
212 
213 		if (e->e_sendmode == SM_DEFER)
214 			msg = "Deferring message until queue run";
215 		if (tTd(20, 1))
216 			sm_dprintf("parseaddr: queuing message\n");
217 		message(msg);
218 		if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
219 			e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);
220 		a->q_state = QS_QUEUEUP;
221 		a->q_status = "4.4.3";
222 	}
223 
224 	/*
225 	**  Compute return value.
226 	*/
227 
228 	if (tTd(20, 1))
229 	{
230 		sm_dprintf("parseaddr-->");
231 		printaddr(a, false);
232 	}
233 
234 	return a;
235 }
236 /*
237 **  INVALIDADDR -- check for address containing characters used for macros
238 **
239 **	Parameters:
240 **		addr -- the address to check.
241 **		delimptr -- if non-NULL: end of address to check, i.e.,
242 **			a pointer in the address string.
243 **		isrcpt -- true iff the address is for a recipient.
244 **
245 **	Returns:
246 **		true -- if the address has characters that are reservered
247 **			for macros or is too long.
248 **		false -- otherwise.
249 */
250 
251 bool
252 invalidaddr(addr, delimptr, isrcpt)
253 	register char *addr;
254 	char *delimptr;
255 	bool isrcpt;
256 {
257 	bool result = false;
258 	char savedelim = '\0';
259 	char *b = addr;
260 	int len = 0;
261 
262 	if (delimptr != NULL)
263 	{
264 		/* delimptr points to the end of the address to test */
265 		savedelim = *delimptr;
266 		if (savedelim != '\0')	/* if that isn't '\0' already: */
267 			*delimptr = '\0';	/* set it */
268 	}
269 	for (; *addr != '\0'; addr++)
270 	{
271 		if ((*addr & 0340) == 0200)
272 		{
273 			setstat(EX_USAGE);
274 			result = true;
275 			*addr = BAD_CHAR_REPLACEMENT;
276 		}
277 		if (++len > MAXNAME - 1)
278 		{
279 			char saved = *addr;
280 
281 			*addr = '\0';
282 			usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
283 			       b, MAXNAME - 1);
284 			*addr = saved;
285 			result = true;
286 			goto delim;
287 		}
288 	}
289 	if (result)
290 	{
291 		if (isrcpt)
292 			usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"",
293 			       b);
294 		else
295 			usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"",
296 			       b);
297 	}
298 delim:
299 	if (delimptr != NULL && savedelim != '\0')
300 		*delimptr = savedelim;	/* restore old character at delimptr */
301 	return result;
302 }
303 /*
304 **  HASCTRLCHAR -- check for address containing meta-characters
305 **
306 **  Checks that the address contains no meta-characters, and contains
307 **  no "non-printable" characters unless they are quoted or escaped.
308 **  Quoted or escaped characters are literals.
309 **
310 **	Parameters:
311 **		addr -- the address to check.
312 **		isrcpt -- true if the address is for a recipient; false
313 **			indicates a from.
314 **		complain -- true if an error should issued if the address
315 **			is invalid and should be "repaired".
316 **
317 **	Returns:
318 **		true -- if the address has any "wierd" characters or
319 **			non-printable characters or if a quote is unbalanced.
320 **		false -- otherwise.
321 */
322 
323 static bool
324 hasctrlchar(addr, isrcpt, complain)
325 	register char *addr;
326 	bool isrcpt, complain;
327 {
328 	bool quoted = false;
329 	int len = 0;
330 	char *result = NULL;
331 	char *b = addr;
332 
333 	if (addr == NULL)
334 		return false;
335 	for (; *addr != '\0'; addr++)
336 	{
337 		if (++len > MAXNAME - 1)
338 		{
339 			if (complain)
340 			{
341 				(void) shorten_rfc822_string(b, MAXNAME - 1);
342 				usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
343 				       b, MAXNAME - 1);
344 				return true;
345 			}
346 			result = "too long";
347 		}
348 		if (!quoted && (*addr < 32 || *addr == 127))
349 		{
350 			result = "non-printable character";
351 			*addr = BAD_CHAR_REPLACEMENT;
352 			continue;
353 		}
354 		if (*addr == '"')
355 			quoted = !quoted;
356 		else if (*addr == '\\')
357 		{
358 			/* XXX Generic problem: no '\0' in strings. */
359 			if (*++addr == '\0')
360 			{
361 				result = "trailing \\ character";
362 				*--addr = BAD_CHAR_REPLACEMENT;
363 				break;
364 			}
365 		}
366 		if ((*addr & 0340) == 0200)
367 		{
368 			setstat(EX_USAGE);
369 			result = "8-bit character";
370 			*addr = BAD_CHAR_REPLACEMENT;
371 			continue;
372 		}
373 	}
374 	if (quoted)
375 		result = "unbalanced quote"; /* unbalanced quote */
376 	if (result != NULL && complain)
377 	{
378 		if (isrcpt)
379 			usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)",
380 			       b, result);
381 		else
382 			usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)",
383 			       b, result);
384 	}
385 	return result != NULL;
386 }
387 /*
388 **  ALLOCADDR -- do local allocations of address on demand.
389 **
390 **	Also lowercases the host name if requested.
391 **
392 **	Parameters:
393 **		a -- the address to reallocate.
394 **		flags -- the copy flag (see RF_ definitions in sendmail.h
395 **			for a description).
396 **		paddr -- the printname of the address.
397 **		e -- envelope
398 **
399 **	Returns:
400 **		none.
401 **
402 **	Side Effects:
403 **		Copies portions of a into local buffers as requested.
404 */
405 
406 static void
407 allocaddr(a, flags, paddr, e)
408 	register ADDRESS *a;
409 	int flags;
410 	char *paddr;
411 	ENVELOPE *e;
412 {
413 	if (tTd(24, 4))
414 		sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
415 
416 	a->q_paddr = paddr;
417 
418 	if (a->q_user == NULL)
419 		a->q_user = "";
420 	if (a->q_host == NULL)
421 		a->q_host = "";
422 
423 	if (bitset(RF_COPYPARSE, flags))
424 	{
425 		a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);
426 		if (a->q_user != a->q_paddr)
427 			a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);
428 	}
429 
430 	if (a->q_paddr == NULL)
431 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
432 	a->q_qgrp = NOAQGRP;
433 }
434 /*
435 **  PRESCAN -- Prescan name and make it canonical
436 **
437 **	Scans a name and turns it into a set of tokens.  This process
438 **	deletes blanks and comments (in parentheses) (if the token type
439 **	for left paren is SPC).
440 **
441 **	This routine knows about quoted strings and angle brackets.
442 **
443 **	There are certain subtleties to this routine.  The one that
444 **	comes to mind now is that backslashes on the ends of names
445 **	are silently stripped off; this is intentional.  The problem
446 **	is that some versions of sndmsg (like at LBL) set the kill
447 **	character to something other than @ when reading addresses;
448 **	so people type "csvax.eric\@berkeley" -- which screws up the
449 **	berknet mailer.
450 **
451 **	Parameters:
452 **		addr -- the name to chomp.
453 **		delim -- the delimiter for the address, normally
454 **			'\0' or ','; \0 is accepted in any case.
455 **			If '\t' then we are reading the .cf file.
456 **		pvpbuf -- place to put the saved text -- note that
457 **			the pointers are static.
458 **		pvpbsize -- size of pvpbuf.
459 **		delimptr -- if non-NULL, set to the location of the
460 **			terminating delimiter.
461 **		toktab -- if set, a token table to use for parsing.
462 **			If NULL, use the default table.
463 **
464 **	Returns:
465 **		A pointer to a vector of tokens.
466 **		NULL on error.
467 */
468 
469 /* states and character types */
470 #define OPR		0	/* operator */
471 #define ATM		1	/* atom */
472 #define QST		2	/* in quoted string */
473 #define SPC		3	/* chewing up spaces */
474 #define ONE		4	/* pick up one character */
475 #define ILL		5	/* illegal character */
476 
477 #define NSTATES	6	/* number of states */
478 #define TYPE		017	/* mask to select state type */
479 
480 /* meta bits for table */
481 #define M		020	/* meta character; don't pass through */
482 #define B		040	/* cause a break */
483 #define MB		M|B	/* meta-break */
484 
485 static short StateTab[NSTATES][NSTATES] =
486 {
487    /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	ILL	*/
488 	/*OPR*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
489 	/*ATM*/	{	OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,	ILL|MB	},
490 	/*QST*/	{	QST,	QST,	OPR,	QST,	QST,	QST	},
491 	/*SPC*/	{	OPR,	ATM,	QST,	SPC|M,	ONE,	ILL|MB	},
492 	/*ONE*/	{	OPR,	OPR,	OPR,	OPR,	OPR,	ILL|MB	},
493 	/*ILL*/	{	OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,	ILL|M	},
494 };
495 
496 /* token type table -- it gets modified with $o characters */
497 static unsigned char	TokTypeTab[256] =
498 {
499     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
500 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
501     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
502 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
503     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
504 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
505     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
506 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
507     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
508 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
509     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
510 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
511     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
512 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
513     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
514 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
515 
516     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
517 	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
518     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
519 	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
520     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
521 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
522     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
523 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
524     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
525 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
526     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
527 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
528     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
529 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
530     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
531 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
532 };
533 
534 /* token type table for MIME parsing */
535 unsigned char	MimeTokenTab[256] =
536 {
537     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
538 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
539     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
540 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
541     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
542 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
543     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
544 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
545     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
546 	OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
547     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
548 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
549     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
550 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
551     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
552 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
553 
554     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
555 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
556     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
557 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
558     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
559 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
560     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
561 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
562     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
563 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
564     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
565 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
566     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
567 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
568     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
569 	ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
570 };
571 
572 /* token type table: don't strip comments */
573 unsigned char	TokTypeNoC[256] =
574 {
575     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
576 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
577     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
578 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
579     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
580 	SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
581     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
582 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
583     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
584 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
585     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
586 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
587     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
588 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
589     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
590 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
591 
592     /*	nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
593 	OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
594     /*	dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
595 	OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
596     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
597 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
598     /*	0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
599 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
600     /*	@   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
601 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
602     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
603 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
604     /*	`   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
605 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
606     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
607 	ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
608 };
609 
610 
611 #define NOCHAR		(-1)	/* signal nothing in lookahead token */
612 
613 char **
614 prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
615 	char *addr;
616 	int delim;
617 	char pvpbuf[];
618 	int pvpbsize;
619 	char **delimptr;
620 	unsigned char *toktab;
621 {
622 	register char *p;
623 	register char *q;
624 	register int c;
625 	char **avp;
626 	bool bslashmode;
627 	bool route_syntax;
628 	int cmntcnt;
629 	int anglecnt;
630 	char *tok;
631 	int state;
632 	int newstate;
633 	char *saveto = CurEnv->e_to;
634 	static char *av[MAXATOM + 1];
635 	static bool firsttime = true;
636 	extern int errno;
637 
638 	if (firsttime)
639 	{
640 		/* initialize the token type table */
641 		char obuf[50];
642 
643 		firsttime = false;
644 		if (OperatorChars == NULL)
645 		{
646 			if (ConfigLevel < 7)
647 				OperatorChars = macvalue('o', CurEnv);
648 			if (OperatorChars == NULL)
649 				OperatorChars = ".:@[]";
650 		}
651 		expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS,
652 		       CurEnv);
653 		(void) sm_strlcat(obuf, DELIMCHARS, sizeof obuf);
654 		for (p = obuf; *p != '\0'; p++)
655 		{
656 			if (TokTypeTab[*p & 0xff] == ATM)
657 				TokTypeTab[*p & 0xff] = OPR;
658 			if (TokTypeNoC[*p & 0xff] == ATM)
659 				TokTypeNoC[*p & 0xff] = OPR;
660 		}
661 	}
662 	if (toktab == NULL)
663 		toktab = TokTypeTab;
664 
665 	/* make sure error messages don't have garbage on them */
666 	errno = 0;
667 
668 	q = pvpbuf;
669 	bslashmode = false;
670 	route_syntax = false;
671 	cmntcnt = 0;
672 	anglecnt = 0;
673 	avp = av;
674 	state = ATM;
675 	c = NOCHAR;
676 	p = addr;
677 	CurEnv->e_to = p;
678 	if (tTd(22, 11))
679 	{
680 		sm_dprintf("prescan: ");
681 		xputs(p);
682 		sm_dprintf("\n");
683 	}
684 
685 	do
686 	{
687 		/* read a token */
688 		tok = q;
689 		for (;;)
690 		{
691 			/* store away any old lookahead character */
692 			if (c != NOCHAR && !bslashmode)
693 			{
694 				/* see if there is room */
695 				if (q >= &pvpbuf[pvpbsize - 5])
696 				{
697 	addrtoolong:
698 					usrerr("553 5.1.1 Address too long");
699 					if (strlen(addr) > MAXNAME)
700 						addr[MAXNAME] = '\0';
701 	returnnull:
702 					if (delimptr != NULL)
703 						*delimptr = p;
704 					CurEnv->e_to = saveto;
705 					return NULL;
706 				}
707 
708 				/* squirrel it away */
709 #if !ALLOW_255
710 				if ((char) c == (char) -1 && !tTd(82, 101))
711 					c &= 0x7f;
712 #endif /* !ALLOW_255 */
713 				*q++ = c;
714 			}
715 
716 			/* read a new input character */
717 			c = (*p++) & 0x00ff;
718 			if (c == '\0')
719 			{
720 				/* diagnose and patch up bad syntax */
721 				if (state == QST)
722 				{
723 					usrerr("553 Unbalanced '\"'");
724 					c = '"';
725 				}
726 				else if (cmntcnt > 0)
727 				{
728 					usrerr("553 Unbalanced '('");
729 					c = ')';
730 				}
731 				else if (anglecnt > 0)
732 				{
733 					c = '>';
734 					usrerr("553 Unbalanced '<'");
735 				}
736 				else
737 					break;
738 
739 				p--;
740 			}
741 			else if (c == delim && cmntcnt <= 0 && state != QST)
742 			{
743 				if (anglecnt <= 0)
744 					break;
745 
746 				/* special case for better error management */
747 				if (delim == ',' && !route_syntax)
748 				{
749 					usrerr("553 Unbalanced '<'");
750 					c = '>';
751 					p--;
752 				}
753 			}
754 
755 			if (tTd(22, 101))
756 				sm_dprintf("c=%c, s=%d; ", c, state);
757 
758 			/* chew up special characters */
759 			*q = '\0';
760 			if (bslashmode)
761 			{
762 				bslashmode = false;
763 
764 				/* kludge \! for naive users */
765 				if (cmntcnt > 0)
766 				{
767 					c = NOCHAR;
768 					continue;
769 				}
770 				else if (c != '!' || state == QST)
771 				{
772 					/* see if there is room */
773 					if (q >= &pvpbuf[pvpbsize - 5])
774 						goto addrtoolong;
775 					*q++ = '\\';
776 					continue;
777 				}
778 			}
779 
780 			if (c == '\\')
781 			{
782 				bslashmode = true;
783 			}
784 			else if (state == QST)
785 			{
786 				/* EMPTY */
787 				/* do nothing, just avoid next clauses */
788 			}
789 			else if (c == '(' && toktab['('] == SPC)
790 			{
791 				cmntcnt++;
792 				c = NOCHAR;
793 			}
794 			else if (c == ')' && toktab['('] == SPC)
795 			{
796 				if (cmntcnt <= 0)
797 				{
798 					usrerr("553 Unbalanced ')'");
799 					c = NOCHAR;
800 				}
801 				else
802 					cmntcnt--;
803 			}
804 			else if (cmntcnt > 0)
805 			{
806 				c = NOCHAR;
807 			}
808 			else if (c == '<')
809 			{
810 				char *ptr = p;
811 
812 				anglecnt++;
813 				while (isascii(*ptr) && isspace(*ptr))
814 					ptr++;
815 				if (*ptr == '@')
816 					route_syntax = true;
817 			}
818 			else if (c == '>')
819 			{
820 				if (anglecnt <= 0)
821 				{
822 					usrerr("553 Unbalanced '>'");
823 					c = NOCHAR;
824 				}
825 				else
826 					anglecnt--;
827 				route_syntax = false;
828 			}
829 			else if (delim == ' ' && isascii(c) && isspace(c))
830 				c = ' ';
831 
832 			if (c == NOCHAR)
833 				continue;
834 
835 			/* see if this is end of input */
836 			if (c == delim && anglecnt <= 0 && state != QST)
837 				break;
838 
839 			newstate = StateTab[state][toktab[c & 0xff]];
840 			if (tTd(22, 101))
841 				sm_dprintf("ns=%02o\n", newstate);
842 			state = newstate & TYPE;
843 			if (state == ILL)
844 			{
845 				if (isascii(c) && isprint(c))
846 					usrerr("553 Illegal character %c", c);
847 				else
848 					usrerr("553 Illegal character 0x%02x",
849 					       c & 0x0ff);
850 			}
851 			if (bitset(M, newstate))
852 				c = NOCHAR;
853 			if (bitset(B, newstate))
854 				break;
855 		}
856 
857 		/* new token */
858 		if (tok != q)
859 		{
860 			/* see if there is room */
861 			if (q >= &pvpbuf[pvpbsize - 5])
862 				goto addrtoolong;
863 			*q++ = '\0';
864 			if (tTd(22, 36))
865 			{
866 				sm_dprintf("tok=");
867 				xputs(tok);
868 				sm_dprintf("\n");
869 			}
870 			if (avp >= &av[MAXATOM])
871 			{
872 				usrerr("553 5.1.0 prescan: too many tokens");
873 				goto returnnull;
874 			}
875 			if (q - tok > MAXNAME)
876 			{
877 				usrerr("553 5.1.0 prescan: token too long");
878 				goto returnnull;
879 			}
880 			*avp++ = tok;
881 		}
882 	} while (c != '\0' && (c != delim || anglecnt > 0));
883 	*avp = NULL;
884 	p--;
885 	if (delimptr != NULL)
886 		*delimptr = p;
887 	if (tTd(22, 12))
888 	{
889 		sm_dprintf("prescan==>");
890 		printav(av);
891 	}
892 	CurEnv->e_to = saveto;
893 	if (av[0] == NULL)
894 	{
895 		if (tTd(22, 1))
896 			sm_dprintf("prescan: null leading token\n");
897 		return NULL;
898 	}
899 	return av;
900 }
901 /*
902 **  REWRITE -- apply rewrite rules to token vector.
903 **
904 **	This routine is an ordered production system.  Each rewrite
905 **	rule has a LHS (called the pattern) and a RHS (called the
906 **	rewrite); 'rwr' points the the current rewrite rule.
907 **
908 **	For each rewrite rule, 'avp' points the address vector we
909 **	are trying to match against, and 'pvp' points to the pattern.
910 **	If pvp points to a special match value (MATCHZANY, MATCHANY,
911 **	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
912 **	matched is saved away in the match vector (pointed to by 'mvp').
913 **
914 **	When a match between avp & pvp does not match, we try to
915 **	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
916 **	we must also back out the match in mvp.  If we reach a
917 **	MATCHANY or MATCHZANY we just extend the match and start
918 **	over again.
919 **
920 **	When we finally match, we rewrite the address vector
921 **	and try over again.
922 **
923 **	Parameters:
924 **		pvp -- pointer to token vector.
925 **		ruleset -- the ruleset to use for rewriting.
926 **		reclevel -- recursion level (to catch loops).
927 **		e -- the current envelope.
928 **		maxatom -- maximum length of buffer (usually MAXATOM)
929 **
930 **	Returns:
931 **		A status code.  If EX_TEMPFAIL, higher level code should
932 **			attempt recovery.
933 **
934 **	Side Effects:
935 **		pvp is modified.
936 */
937 
938 struct match
939 {
940 	char	**match_first;		/* first token matched */
941 	char	**match_last;		/* last token matched */
942 	char	**match_pattern;	/* pointer to pattern */
943 };
944 
945 int
946 rewrite(pvp, ruleset, reclevel, e, maxatom)
947 	char **pvp;
948 	int ruleset;
949 	int reclevel;
950 	register ENVELOPE *e;
951 	int maxatom;
952 {
953 	register char *ap;		/* address pointer */
954 	register char *rp;		/* rewrite pointer */
955 	register char *rulename;	/* ruleset name */
956 	register char *prefix;
957 	register char **avp;		/* address vector pointer */
958 	register char **rvp;		/* rewrite vector pointer */
959 	register struct match *mlp;	/* cur ptr into mlist */
960 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
961 	int ruleno;			/* current rule number */
962 	int rstat = EX_OK;		/* return status */
963 	int loopcount;
964 	struct match mlist[MAXMATCH];	/* stores match on LHS */
965 	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */
966 	char buf[MAXLINE];
967 	char name[6];
968 
969 	if (ruleset < 0 || ruleset >= MAXRWSETS)
970 	{
971 		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
972 		return EX_CONFIG;
973 	}
974 	rulename = RuleSetNames[ruleset];
975 	if (rulename == NULL)
976 	{
977 		(void) sm_snprintf(name, sizeof name, "%d", ruleset);
978 		rulename = name;
979 	}
980 	if (OpMode == MD_TEST)
981 		prefix = "";
982 	else
983 		prefix = "rewrite: ruleset ";
984 	if (OpMode == MD_TEST)
985 	{
986 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
987 				     "%s%-16.16s   input:", prefix, rulename);
988 		printav(pvp);
989 	}
990 	else if (tTd(21, 1))
991 	{
992 		sm_dprintf("%s%-16.16s   input:", prefix, rulename);
993 		printav(pvp);
994 	}
995 	if (reclevel++ > MaxRuleRecursion)
996 	{
997 		syserr("rewrite: excessive recursion (max %d), ruleset %s",
998 			MaxRuleRecursion, rulename);
999 		return EX_CONFIG;
1000 	}
1001 	if (pvp == NULL)
1002 		return EX_USAGE;
1003 
1004 	/*
1005 	**  Run through the list of rewrite rules, applying
1006 	**	any that match.
1007 	*/
1008 
1009 	ruleno = 1;
1010 	loopcount = 0;
1011 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
1012 	{
1013 		int status;
1014 
1015 		/* if already canonical, quit now */
1016 		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
1017 			break;
1018 
1019 		if (tTd(21, 12))
1020 		{
1021 			if (tTd(21, 15))
1022 				sm_dprintf("-----trying rule (line %d):",
1023 				       rwr->r_line);
1024 			else
1025 				sm_dprintf("-----trying rule:");
1026 			printav(rwr->r_lhs);
1027 		}
1028 
1029 		/* try to match on this rule */
1030 		mlp = mlist;
1031 		rvp = rwr->r_lhs;
1032 		avp = pvp;
1033 		if (++loopcount > 100)
1034 		{
1035 			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
1036 				rulename, ruleno);
1037 			if (tTd(21, 1))
1038 			{
1039 				sm_dprintf("workspace: ");
1040 				printav(pvp);
1041 			}
1042 			break;
1043 		}
1044 
1045 		while ((ap = *avp) != NULL || *rvp != NULL)
1046 		{
1047 			rp = *rvp;
1048 			if (tTd(21, 35))
1049 			{
1050 				sm_dprintf("ADVANCE rp=");
1051 				xputs(rp);
1052 				sm_dprintf(", ap=");
1053 				xputs(ap);
1054 				sm_dprintf("\n");
1055 			}
1056 			if (rp == NULL)
1057 			{
1058 				/* end-of-pattern before end-of-address */
1059 				goto backup;
1060 			}
1061 			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
1062 			    (*rp & 0377) != MATCHZERO)
1063 			{
1064 				/* end-of-input with patterns left */
1065 				goto backup;
1066 			}
1067 
1068 			switch (*rp & 0377)
1069 			{
1070 			  case MATCHCLASS:
1071 				/* match any phrase in a class */
1072 				mlp->match_pattern = rvp;
1073 				mlp->match_first = avp;
1074 	extendclass:
1075 				ap = *avp;
1076 				if (ap == NULL)
1077 					goto backup;
1078 				mlp->match_last = avp++;
1079 				cataddr(mlp->match_first, mlp->match_last,
1080 					buf, sizeof buf, '\0');
1081 				if (!wordinclass(buf, rp[1]))
1082 				{
1083 					if (tTd(21, 36))
1084 					{
1085 						sm_dprintf("EXTEND  rp=");
1086 						xputs(rp);
1087 						sm_dprintf(", ap=");
1088 						xputs(ap);
1089 						sm_dprintf("\n");
1090 					}
1091 					goto extendclass;
1092 				}
1093 				if (tTd(21, 36))
1094 					sm_dprintf("CLMATCH\n");
1095 				mlp++;
1096 				break;
1097 
1098 			  case MATCHNCLASS:
1099 				/* match any token not in a class */
1100 				if (wordinclass(ap, rp[1]))
1101 					goto backup;
1102 
1103 				/* FALLTHROUGH */
1104 
1105 			  case MATCHONE:
1106 			  case MATCHANY:
1107 				/* match exactly one token */
1108 				mlp->match_pattern = rvp;
1109 				mlp->match_first = avp;
1110 				mlp->match_last = avp++;
1111 				mlp++;
1112 				break;
1113 
1114 			  case MATCHZANY:
1115 				/* match zero or more tokens */
1116 				mlp->match_pattern = rvp;
1117 				mlp->match_first = avp;
1118 				mlp->match_last = avp - 1;
1119 				mlp++;
1120 				break;
1121 
1122 			  case MATCHZERO:
1123 				/* match zero tokens */
1124 				break;
1125 
1126 			  case MACRODEXPAND:
1127 				/*
1128 				**  Match against run-time macro.
1129 				**  This algorithm is broken for the
1130 				**  general case (no recursive macros,
1131 				**  improper tokenization) but should
1132 				**  work for the usual cases.
1133 				*/
1134 
1135 				ap = macvalue(rp[1], e);
1136 				mlp->match_first = avp;
1137 				if (tTd(21, 2))
1138 					sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",
1139 						macname(rp[1]),
1140 						ap == NULL ? "(NULL)" : ap);
1141 
1142 				if (ap == NULL)
1143 					break;
1144 				while (*ap != '\0')
1145 				{
1146 					if (*avp == NULL ||
1147 					    sm_strncasecmp(ap, *avp,
1148 							   strlen(*avp)) != 0)
1149 					{
1150 						/* no match */
1151 						avp = mlp->match_first;
1152 						goto backup;
1153 					}
1154 					ap += strlen(*avp++);
1155 				}
1156 
1157 				/* match */
1158 				break;
1159 
1160 			  default:
1161 				/* must have exact match */
1162 				if (sm_strcasecmp(rp, ap))
1163 					goto backup;
1164 				avp++;
1165 				break;
1166 			}
1167 
1168 			/* successful match on this token */
1169 			rvp++;
1170 			continue;
1171 
1172 	  backup:
1173 			/* match failed -- back up */
1174 			while (--mlp >= mlist)
1175 			{
1176 				rvp = mlp->match_pattern;
1177 				rp = *rvp;
1178 				avp = mlp->match_last + 1;
1179 				ap = *avp;
1180 
1181 				if (tTd(21, 36))
1182 				{
1183 					sm_dprintf("BACKUP  rp=");
1184 					xputs(rp);
1185 					sm_dprintf(", ap=");
1186 					xputs(ap);
1187 					sm_dprintf("\n");
1188 				}
1189 
1190 				if (ap == NULL)
1191 				{
1192 					/* run off the end -- back up again */
1193 					continue;
1194 				}
1195 				if ((*rp & 0377) == MATCHANY ||
1196 				    (*rp & 0377) == MATCHZANY)
1197 				{
1198 					/* extend binding and continue */
1199 					mlp->match_last = avp++;
1200 					rvp++;
1201 					mlp++;
1202 					break;
1203 				}
1204 				if ((*rp & 0377) == MATCHCLASS)
1205 				{
1206 					/* extend binding and try again */
1207 					mlp->match_last = avp;
1208 					goto extendclass;
1209 				}
1210 			}
1211 
1212 			if (mlp < mlist)
1213 			{
1214 				/* total failure to match */
1215 				break;
1216 			}
1217 		}
1218 
1219 		/*
1220 		**  See if we successfully matched
1221 		*/
1222 
1223 		if (mlp < mlist || *rvp != NULL)
1224 		{
1225 			if (tTd(21, 10))
1226 				sm_dprintf("----- rule fails\n");
1227 			rwr = rwr->r_next;
1228 			ruleno++;
1229 			loopcount = 0;
1230 			continue;
1231 		}
1232 
1233 		rvp = rwr->r_rhs;
1234 		if (tTd(21, 12))
1235 		{
1236 			sm_dprintf("-----rule matches:");
1237 			printav(rvp);
1238 		}
1239 
1240 		rp = *rvp;
1241 		if (rp != NULL)
1242 		{
1243 			if ((*rp & 0377) == CANONUSER)
1244 			{
1245 				rvp++;
1246 				rwr = rwr->r_next;
1247 				ruleno++;
1248 				loopcount = 0;
1249 			}
1250 			else if ((*rp & 0377) == CANONHOST)
1251 			{
1252 				rvp++;
1253 				rwr = NULL;
1254 			}
1255 		}
1256 
1257 		/* substitute */
1258 		for (avp = npvp; *rvp != NULL; rvp++)
1259 		{
1260 			register struct match *m;
1261 			register char **pp;
1262 
1263 			rp = *rvp;
1264 			if ((*rp & 0377) == MATCHREPL)
1265 			{
1266 				/* substitute from LHS */
1267 				m = &mlist[rp[1] - '1'];
1268 				if (m < mlist || m >= mlp)
1269 				{
1270 					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
1271 						rulename, rp[1]);
1272 					return EX_CONFIG;
1273 				}
1274 				if (tTd(21, 15))
1275 				{
1276 					sm_dprintf("$%c:", rp[1]);
1277 					pp = m->match_first;
1278 					while (pp <= m->match_last)
1279 					{
1280 						sm_dprintf(" %p=\"", *pp);
1281 						sm_dflush();
1282 						sm_dprintf("%s\"", *pp++);
1283 					}
1284 					sm_dprintf("\n");
1285 				}
1286 				pp = m->match_first;
1287 				while (pp <= m->match_last)
1288 				{
1289 					if (avp >= &npvp[maxatom])
1290 					{
1291 						syserr("554 5.3.0 rewrite: expansion too long");
1292 						if (LogLevel > 9)
1293 							sm_syslog(LOG_ERR,
1294 								e->e_id,
1295 								"rewrite: expansion too long, ruleset=%s, ruleno=%d",
1296 								rulename,
1297 								ruleno);
1298 						return EX_DATAERR;
1299 					}
1300 					*avp++ = *pp++;
1301 				}
1302 			}
1303 			else
1304 			{
1305 				/* some sort of replacement */
1306 				if (avp >= &npvp[maxatom])
1307 				{
1308 	toolong:
1309 					syserr("554 5.3.0 rewrite: expansion too long");
1310 					if (LogLevel > 9)
1311 						sm_syslog(LOG_ERR, e->e_id,
1312 							"rewrite: expansion too long, ruleset=%s, ruleno=%d",
1313 							rulename, ruleno);
1314 					return EX_DATAERR;
1315 				}
1316 				if ((*rp & 0377) != MACRODEXPAND)
1317 				{
1318 					/* vanilla replacement */
1319 					*avp++ = rp;
1320 				}
1321 				else
1322 				{
1323 					/* $&{x} replacement */
1324 					char *mval = macvalue(rp[1], e);
1325 					char **xpvp;
1326 					int trsize = 0;
1327 					static size_t pvpb1_size = 0;
1328 					static char **pvpb1 = NULL;
1329 					char pvpbuf[PSBUFSIZE];
1330 
1331 					if (tTd(21, 2))
1332 						sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
1333 							macname(rp[1]),
1334 							mval == NULL ? "(NULL)" : mval);
1335 					if (mval == NULL || *mval == '\0')
1336 						continue;
1337 
1338 					/* save the remainder of the input */
1339 					for (xpvp = pvp; *xpvp != NULL; xpvp++)
1340 						trsize += sizeof *xpvp;
1341 					if ((size_t) trsize > pvpb1_size)
1342 					{
1343 						if (pvpb1 != NULL)
1344 							sm_free(pvpb1);
1345 						pvpb1 = (char **)
1346 							sm_pmalloc_x(trsize);
1347 						pvpb1_size = trsize;
1348 					}
1349 
1350 					memmove((char *) pvpb1,
1351 						(char *) pvp,
1352 						trsize);
1353 
1354 					/* scan the new replacement */
1355 					xpvp = prescan(mval, '\0', pvpbuf,
1356 						       sizeof pvpbuf, NULL,
1357 						       NULL);
1358 					if (xpvp == NULL)
1359 					{
1360 						/* prescan pre-printed error */
1361 						return EX_DATAERR;
1362 					}
1363 
1364 					/* insert it into the output stream */
1365 					while (*xpvp != NULL)
1366 					{
1367 						if (tTd(21, 19))
1368 							sm_dprintf(" ... %s\n",
1369 								*xpvp);
1370 						*avp++ = sm_rpool_strdup_x(
1371 							e->e_rpool, *xpvp);
1372 						if (avp >= &npvp[maxatom])
1373 							goto toolong;
1374 						xpvp++;
1375 					}
1376 					if (tTd(21, 19))
1377 						sm_dprintf(" ... DONE\n");
1378 
1379 					/* restore the old trailing input */
1380 					memmove((char *) pvp,
1381 						(char *) pvpb1,
1382 						trsize);
1383 				}
1384 			}
1385 		}
1386 		*avp++ = NULL;
1387 
1388 		/*
1389 		**  Check for any hostname/keyword lookups.
1390 		*/
1391 
1392 		for (rvp = npvp; *rvp != NULL; rvp++)
1393 		{
1394 			char **hbrvp;
1395 			char **xpvp;
1396 			int trsize;
1397 			char *replac;
1398 			int endtoken;
1399 			STAB *map;
1400 			char *mapname;
1401 			char **key_rvp;
1402 			char **arg_rvp;
1403 			char **default_rvp;
1404 			char cbuf[MAXNAME + 1];
1405 			char *pvpb1[MAXATOM + 1];
1406 			char *argvect[10];
1407 			char pvpbuf[PSBUFSIZE];
1408 			char *nullpvp[1];
1409 
1410 			if ((**rvp & 0377) != HOSTBEGIN &&
1411 			    (**rvp & 0377) != LOOKUPBEGIN)
1412 				continue;
1413 
1414 			/*
1415 			**  Got a hostname/keyword lookup.
1416 			**
1417 			**	This could be optimized fairly easily.
1418 			*/
1419 
1420 			hbrvp = rvp;
1421 			if ((**rvp & 0377) == HOSTBEGIN)
1422 			{
1423 				endtoken = HOSTEND;
1424 				mapname = "host";
1425 			}
1426 			else
1427 			{
1428 				endtoken = LOOKUPEND;
1429 				mapname = *++rvp;
1430 			}
1431 			map = stab(mapname, ST_MAP, ST_FIND);
1432 			if (map == NULL)
1433 				syserr("554 5.3.0 rewrite: map %s not found", mapname);
1434 
1435 			/* extract the match part */
1436 			key_rvp = ++rvp;
1437 			default_rvp = NULL;
1438 			arg_rvp = argvect;
1439 			xpvp = NULL;
1440 			replac = pvpbuf;
1441 			while (*rvp != NULL && (**rvp & 0377) != endtoken)
1442 			{
1443 				int nodetype = **rvp & 0377;
1444 
1445 				if (nodetype != CANONHOST && nodetype != CANONUSER)
1446 				{
1447 					rvp++;
1448 					continue;
1449 				}
1450 
1451 				*rvp++ = NULL;
1452 
1453 				if (xpvp != NULL)
1454 				{
1455 					cataddr(xpvp, NULL, replac,
1456 						&pvpbuf[sizeof pvpbuf] - replac,
1457 						'\0');
1458 					*++arg_rvp = replac;
1459 					replac += strlen(replac) + 1;
1460 					xpvp = NULL;
1461 				}
1462 				switch (nodetype)
1463 				{
1464 				  case CANONHOST:
1465 					xpvp = rvp;
1466 					break;
1467 
1468 				  case CANONUSER:
1469 					default_rvp = rvp;
1470 					break;
1471 				}
1472 			}
1473 			if (*rvp != NULL)
1474 				*rvp++ = NULL;
1475 			if (xpvp != NULL)
1476 			{
1477 				cataddr(xpvp, NULL, replac,
1478 					&pvpbuf[sizeof pvpbuf] - replac,
1479 					'\0');
1480 				*++arg_rvp = replac;
1481 			}
1482 			*++arg_rvp = NULL;
1483 
1484 			/* save the remainder of the input string */
1485 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1486 			memmove((char *) pvpb1, (char *) rvp, trsize);
1487 
1488 			/* look it up */
1489 			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
1490 				map == NULL ? '\0' : map->s_map.map_spacesub);
1491 			argvect[0] = cbuf;
1492 			replac = map_lookup(map, cbuf, argvect, &rstat, e);
1493 
1494 			/* if no replacement, use default */
1495 			if (replac == NULL && default_rvp != NULL)
1496 			{
1497 				/* create the default */
1498 				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
1499 				replac = cbuf;
1500 			}
1501 
1502 			if (replac == NULL)
1503 			{
1504 				xpvp = key_rvp;
1505 			}
1506 			else if (*replac == '\0')
1507 			{
1508 				/* null replacement */
1509 				nullpvp[0] = NULL;
1510 				xpvp = nullpvp;
1511 			}
1512 			else
1513 			{
1514 				/* scan the new replacement */
1515 				xpvp = prescan(replac, '\0', pvpbuf,
1516 					       sizeof pvpbuf, NULL, NULL);
1517 				if (xpvp == NULL)
1518 				{
1519 					/* prescan already printed error */
1520 					return EX_DATAERR;
1521 				}
1522 			}
1523 
1524 			/* append it to the token list */
1525 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
1526 			{
1527 				*avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
1528 				if (avp >= &npvp[maxatom])
1529 					goto toolong;
1530 			}
1531 
1532 			/* restore the old trailing information */
1533 			rvp = avp - 1;
1534 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1535 				if (avp >= &npvp[maxatom])
1536 					goto toolong;
1537 		}
1538 
1539 		/*
1540 		**  Check for subroutine calls.
1541 		*/
1542 
1543 		status = callsubr(npvp, reclevel, e);
1544 		if (rstat == EX_OK || status == EX_TEMPFAIL)
1545 			rstat = status;
1546 
1547 		/* copy vector back into original space. */
1548 		for (avp = npvp; *avp++ != NULL;)
1549 			continue;
1550 		memmove((char *) pvp, (char *) npvp,
1551 		      (int) (avp - npvp) * sizeof *avp);
1552 
1553 		if (tTd(21, 4))
1554 		{
1555 			sm_dprintf("rewritten as:");
1556 			printav(pvp);
1557 		}
1558 	}
1559 
1560 	if (OpMode == MD_TEST)
1561 	{
1562 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1563 				     "%s%-16.16s returns:", prefix, rulename);
1564 		printav(pvp);
1565 	}
1566 	else if (tTd(21, 1))
1567 	{
1568 		sm_dprintf("%s%-16.16s returns:", prefix, rulename);
1569 		printav(pvp);
1570 	}
1571 	return rstat;
1572 }
1573 /*
1574 **  CALLSUBR -- call subroutines in rewrite vector
1575 **
1576 **	Parameters:
1577 **		pvp -- pointer to token vector.
1578 **		reclevel -- the current recursion level.
1579 **		e -- the current envelope.
1580 **
1581 **	Returns:
1582 **		The status from the subroutine call.
1583 **
1584 **	Side Effects:
1585 **		pvp is modified.
1586 */
1587 
1588 static int
1589 callsubr(pvp, reclevel, e)
1590 	char **pvp;
1591 	int reclevel;
1592 	ENVELOPE *e;
1593 {
1594 	char **avp;
1595 	register int i;
1596 	int subr, j;
1597 	int nsubr;
1598 	int status;
1599 	int rstat = EX_OK;
1600 #define MAX_SUBR	16
1601 	int subrnumber[MAX_SUBR];
1602 	int subrindex[MAX_SUBR];
1603 
1604 	nsubr = 0;
1605 
1606 	/*
1607 	**  Look for subroutine calls in pvp, collect them into subr*[]
1608 	**  We will perform the calls in the next loop, because we will
1609 	**  call the "last" subroutine first to avoid recursive calls
1610 	**  and too much copying.
1611 	*/
1612 
1613 	for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
1614 	{
1615 		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
1616 		{
1617 			stripquotes(avp[1]);
1618 			subr = strtorwset(avp[1], NULL, ST_FIND);
1619 			if (subr < 0)
1620 			{
1621 				syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
1622 				return EX_CONFIG;
1623 			}
1624 
1625 			/*
1626 			**  XXX instead of doing this we could optimize
1627 			**  the rules after reading them: just remove
1628 			**  calls to empty rulesets
1629 			*/
1630 
1631 			/* subroutine is an empty ruleset?  don't call it */
1632 			if (RewriteRules[subr] == NULL)
1633 			{
1634 				if (tTd(21, 3))
1635 					sm_dprintf("-----skip subr %s (%d)\n",
1636 						avp[1], subr);
1637 				for (i = 2; avp[i] != NULL; i++)
1638 					avp[i - 2] = avp[i];
1639 				avp[i - 2] = NULL;
1640 				continue;
1641 			}
1642 			if (++nsubr >= MAX_SUBR)
1643 			{
1644 				syserr("554 5.3.0 Too many subroutine calls (%d max)",
1645 					MAX_SUBR);
1646 				return EX_CONFIG;
1647 			}
1648 			subrnumber[nsubr] = subr;
1649 			subrindex[nsubr] = j;
1650 		}
1651 	}
1652 
1653 	/*
1654 	**  Perform the actual subroutines calls, "last" one first, i.e.,
1655 	**  go from the right to the left through all calls,
1656 	**  do the rewriting in place.
1657 	*/
1658 
1659 	for (; nsubr > 0; nsubr--)
1660 	{
1661 		subr = subrnumber[nsubr];
1662 		avp = pvp + subrindex[nsubr];
1663 
1664 		/* remove the subroutine call and name */
1665 		for (i = 2; avp[i] != NULL; i++)
1666 			avp[i - 2] = avp[i];
1667 		avp[i - 2] = NULL;
1668 
1669 		/*
1670 		**  Now we need to call the ruleset specified for
1671 		**  the subroutine. we can do this inplace since
1672 		**  we call the "last" subroutine first.
1673 		*/
1674 
1675 		status = rewrite(avp, subr, reclevel, e,
1676 				MAXATOM - subrindex[nsubr]);
1677 		if (status != EX_OK && status != EX_TEMPFAIL)
1678 			return status;
1679 		if (rstat == EX_OK || status == EX_TEMPFAIL)
1680 			rstat = status;
1681 	}
1682 	return rstat;
1683 }
1684 /*
1685 **  MAP_LOOKUP -- do lookup in map
1686 **
1687 **	Parameters:
1688 **		smap -- the map to use for the lookup.
1689 **		key -- the key to look up.
1690 **		argvect -- arguments to pass to the map lookup.
1691 **		pstat -- a pointer to an integer in which to store the
1692 **			status from the lookup.
1693 **		e -- the current envelope.
1694 **
1695 **	Returns:
1696 **		The result of the lookup.
1697 **		NULL -- if there was no data for the given key.
1698 */
1699 
1700 static char *
1701 map_lookup(smap, key, argvect, pstat, e)
1702 	STAB *smap;
1703 	char key[];
1704 	char **argvect;
1705 	int *pstat;
1706 	ENVELOPE *e;
1707 {
1708 	auto int status = EX_OK;
1709 	MAP *map;
1710 	char *replac;
1711 
1712 	if (smap == NULL)
1713 		return NULL;
1714 
1715 	map = &smap->s_map;
1716 	DYNOPENMAP(map);
1717 
1718 	if (e->e_sendmode == SM_DEFER &&
1719 	    bitset(MF_DEFER, map->map_mflags))
1720 	{
1721 		/* don't do any map lookups */
1722 		if (tTd(60, 1))
1723 			sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
1724 				smap->s_name, key);
1725 		*pstat = EX_TEMPFAIL;
1726 		return NULL;
1727 	}
1728 
1729 	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
1730 		stripquotes(key);
1731 
1732 	if (tTd(60, 1))
1733 	{
1734 		sm_dprintf("map_lookup(%s, %s", smap->s_name, key);
1735 		if (tTd(60, 5))
1736 		{
1737 			int i;
1738 
1739 			for (i = 0; argvect[i] != NULL; i++)
1740 				sm_dprintf(", %%%d=%s", i, argvect[i]);
1741 		}
1742 		sm_dprintf(") => ");
1743 	}
1744 	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
1745 	if (tTd(60, 1))
1746 		sm_dprintf("%s (%d)\n",
1747 			replac != NULL ? replac : "NOT FOUND",
1748 			status);
1749 
1750 	/* should recover if status == EX_TEMPFAIL */
1751 	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
1752 	{
1753 		*pstat = EX_TEMPFAIL;
1754 		if (tTd(60, 1))
1755 			sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
1756 				smap->s_name, key, errno);
1757 		if (e->e_message == NULL)
1758 		{
1759 			char mbuf[320];
1760 
1761 			(void) sm_snprintf(mbuf, sizeof mbuf,
1762 				"%.80s map: lookup (%s): deferred",
1763 				smap->s_name,
1764 				shortenstring(key, MAXSHORTSTR));
1765 			e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
1766 		}
1767 	}
1768 	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
1769 	{
1770 		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
1771 		static char *rwbuf = NULL;
1772 		static size_t rwbuflen = 0;
1773 
1774 		if (i > rwbuflen)
1775 		{
1776 			if (rwbuf != NULL)
1777 				sm_free(rwbuf);
1778 			rwbuflen = i;
1779 			rwbuf = (char *) sm_pmalloc_x(rwbuflen);
1780 		}
1781 		(void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
1782 		if (tTd(60, 4))
1783 			sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
1784 				rwbuf);
1785 		return rwbuf;
1786 	}
1787 	return replac;
1788 }
1789 /*
1790 **  INITERRMAILERS -- initialize error and discard mailers
1791 **
1792 **	Parameters:
1793 **		none.
1794 **
1795 **	Returns:
1796 **		none.
1797 **
1798 **	Side Effects:
1799 **		initializes error and discard mailers.
1800 */
1801 
1802 static MAILER discardmailer;
1803 static MAILER errormailer;
1804 static char *discardargv[] = { "DISCARD", NULL };
1805 static char *errorargv[] = { "ERROR", NULL };
1806 
1807 void
1808 initerrmailers()
1809 {
1810 	if (discardmailer.m_name == NULL)
1811 	{
1812 		/* initialize the discard mailer */
1813 		discardmailer.m_name = "*discard*";
1814 		discardmailer.m_mailer = "DISCARD";
1815 		discardmailer.m_argv = discardargv;
1816 	}
1817 	if (errormailer.m_name == NULL)
1818 	{
1819 		/* initialize the bogus mailer */
1820 		errormailer.m_name = "*error*";
1821 		errormailer.m_mailer = "ERROR";
1822 		errormailer.m_argv = errorargv;
1823 	}
1824 }
1825 /*
1826 **  BUILDADDR -- build address from token vector.
1827 **
1828 **	Parameters:
1829 **		tv -- token vector.
1830 **		a -- pointer to address descriptor to fill.
1831 **			If NULL, one will be allocated.
1832 **		flags -- info regarding whether this is a sender or
1833 **			a recipient.
1834 **		e -- the current envelope.
1835 **
1836 **	Returns:
1837 **		NULL if there was an error.
1838 **		'a' otherwise.
1839 **
1840 **	Side Effects:
1841 **		fills in 'a'
1842 */
1843 
1844 static struct errcodes
1845 {
1846 	char	*ec_name;		/* name of error code */
1847 	int	ec_code;		/* numeric code */
1848 } ErrorCodes[] =
1849 {
1850 	{ "usage",		EX_USAGE	},
1851 	{ "nouser",		EX_NOUSER	},
1852 	{ "nohost",		EX_NOHOST	},
1853 	{ "unavailable",	EX_UNAVAILABLE	},
1854 	{ "software",		EX_SOFTWARE	},
1855 	{ "tempfail",		EX_TEMPFAIL	},
1856 	{ "protocol",		EX_PROTOCOL	},
1857 	{ "config",		EX_CONFIG	},
1858 	{ NULL,			EX_UNAVAILABLE	}
1859 };
1860 
1861 static ADDRESS *
1862 buildaddr(tv, a, flags, e)
1863 	register char **tv;
1864 	register ADDRESS *a;
1865 	int flags;
1866 	register ENVELOPE *e;
1867 {
1868 	bool tempfail = false;
1869 	struct mailer **mp;
1870 	register struct mailer *m;
1871 	register char *p;
1872 	char *mname;
1873 	char **hostp;
1874 	char hbuf[MAXNAME + 1];
1875 	static char ubuf[MAXNAME + 2];
1876 
1877 	if (tTd(24, 5))
1878 	{
1879 		sm_dprintf("buildaddr, flags=%x, tv=", flags);
1880 		printav(tv);
1881 	}
1882 
1883 	if (a == NULL)
1884 		a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
1885 	memset((char *) a, '\0', sizeof *a);
1886 	hbuf[0] = '\0';
1887 
1888 	/* set up default error return flags */
1889 	a->q_flags |= DefaultNotify;
1890 
1891 	/* figure out what net/mailer to use */
1892 	if (*tv == NULL || (**tv & 0377) != CANONNET)
1893 	{
1894 		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
1895 badaddr:
1896 #if _FFR_ALLOW_S0_ERROR_4XX
1897 		/*
1898 		**  ExitStat may have been set by an earlier map open
1899 		**  failure (to a permanent error (EX_OSERR) in syserr())
1900 		**  so we also need to check if this particular $#error
1901 		**  return wanted a 4XX failure.
1902 		**
1903 		**  XXX the real fix is probably to set ExitStat correctly,
1904 		**  i.e., to EX_TEMPFAIL if the map open is just a temporary
1905 		**  error.
1906 		**
1907 		**  tempfail is tested here even if _FFR_ALLOW_S0_ERROR_4XX
1908 		**  is not set; that's ok because it is initialized to false.
1909 		*/
1910 #endif /* _FFR_ALLOW_S0_ERROR_4XX */
1911 
1912 		if (ExitStat == EX_TEMPFAIL || tempfail)
1913 			a->q_state = QS_QUEUEUP;
1914 		else
1915 		{
1916 			a->q_state = QS_BADADDR;
1917 			a->q_mailer = &errormailer;
1918 		}
1919 		return a;
1920 	}
1921 	mname = *++tv;
1922 
1923 	/* extract host and user portions */
1924 	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1925 		hostp = ++tv;
1926 	else
1927 		hostp = NULL;
1928 	while (*tv != NULL && (**tv & 0377) != CANONUSER)
1929 		tv++;
1930 	if (*tv == NULL)
1931 	{
1932 		syserr("554 5.3.5 buildaddr: no user");
1933 		goto badaddr;
1934 	}
1935 	if (tv == hostp)
1936 		hostp = NULL;
1937 	else if (hostp != NULL)
1938 		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1939 	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1940 
1941 	/* save away the host name */
1942 	if (sm_strcasecmp(mname, "error") == 0)
1943 	{
1944 		/* Set up triplet for use by -bv */
1945 		a->q_mailer = &errormailer;
1946 		a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
1947 		/* XXX wrong place? */
1948 
1949 		if (hostp != NULL)
1950 		{
1951 			register struct errcodes *ep;
1952 
1953 			a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
1954 			if (strchr(hbuf, '.') != NULL)
1955 			{
1956 				a->q_status = sm_rpool_strdup_x(e->e_rpool,
1957 								hbuf);
1958 				setstat(dsntoexitstat(hbuf));
1959 			}
1960 			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
1961 			{
1962 				setstat(atoi(hbuf));
1963 			}
1964 			else
1965 			{
1966 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1967 					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
1968 						break;
1969 				setstat(ep->ec_code);
1970 			}
1971 		}
1972 		else
1973 		{
1974 			a->q_host = NULL;
1975 			setstat(EX_UNAVAILABLE);
1976 		}
1977 		stripquotes(ubuf);
1978 		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
1979 		{
1980 			char fmt[16];
1981 			int off;
1982 
1983 			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
1984 			{
1985 				ubuf[off + 4] = '\0';
1986 				off += 5;
1987 			}
1988 			else
1989 			{
1990 				off = 4;
1991 				ubuf[3] = '\0';
1992 			}
1993 			(void) sm_strlcpyn(fmt, sizeof fmt, 2, ubuf, " %s");
1994 			if (off > 4)
1995 				usrerr(fmt, ubuf + off);
1996 			else if (isenhsc(hbuf, '\0') > 0)
1997 				usrerrenh(hbuf, fmt, ubuf + off);
1998 			else
1999 				usrerr(fmt, ubuf + off);
2000 			/* XXX ubuf[off - 1] = ' '; */
2001 #if _FFR_ALLOW_S0_ERROR_4XX
2002 			if (ubuf[0] == '4')
2003 				tempfail = true;
2004 #endif /* _FFR_ALLOW_S0_ERROR_4XX */
2005 		}
2006 		else
2007 		{
2008 			usrerr("553 5.3.0 %s", ubuf);
2009 		}
2010 		goto badaddr;
2011 	}
2012 
2013 	for (mp = Mailer; (m = *mp++) != NULL; )
2014 	{
2015 		if (sm_strcasecmp(m->m_name, mname) == 0)
2016 			break;
2017 	}
2018 	if (m == NULL)
2019 	{
2020 		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
2021 		goto badaddr;
2022 	}
2023 	a->q_mailer = m;
2024 
2025 	/* figure out what host (if any) */
2026 	if (hostp == NULL)
2027 	{
2028 		if (!bitnset(M_LOCALMAILER, m->m_flags))
2029 		{
2030 			syserr("554 5.3.5 buildaddr: no host");
2031 			goto badaddr;
2032 		}
2033 		a->q_host = NULL;
2034 	}
2035 	else
2036 		a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
2037 
2038 	/* figure out the user */
2039 	p = ubuf;
2040 	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
2041 	{
2042 		p++;
2043 		tv++;
2044 		a->q_flags |= QNOTREMOTE;
2045 	}
2046 
2047 	/* do special mapping for local mailer */
2048 	if (*p == '"')
2049 		p++;
2050 	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
2051 		a->q_mailer = m = ProgMailer;
2052 	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
2053 		a->q_mailer = m = FileMailer;
2054 	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
2055 	{
2056 		/* may be :include: */
2057 		stripquotes(ubuf);
2058 		if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
2059 		{
2060 			/* if :include:, don't need further rewriting */
2061 			a->q_mailer = m = InclMailer;
2062 			a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
2063 			return a;
2064 		}
2065 	}
2066 
2067 	/* rewrite according recipient mailer rewriting rules */
2068 	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2069 
2070 	if (ConfigLevel >= 10 ||
2071 	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
2072 	{
2073 		/* sender addresses done later */
2074 		(void) REWRITE(tv, 2, e);
2075 		if (m->m_re_rwset > 0)
2076 		       (void) REWRITE(tv, m->m_re_rwset, e);
2077 	}
2078 	(void) REWRITE(tv, 4, e);
2079 
2080 	/* save the result for the command line/RCPT argument */
2081 	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
2082 	a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
2083 
2084 	/*
2085 	**  Do mapping to lower case as requested by mailer
2086 	*/
2087 
2088 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
2089 		makelower(a->q_host);
2090 	if (!bitnset(M_USR_UPPER, m->m_flags))
2091 		makelower(a->q_user);
2092 
2093 	if (tTd(24, 6))
2094 	{
2095 		sm_dprintf("buildaddr => ");
2096 		printaddr(a, false);
2097 	}
2098 	return a;
2099 }
2100 
2101 /*
2102 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
2103 **
2104 **	Parameters:
2105 **		pvp -- parameter vector to rebuild.
2106 **		evp -- last parameter to include.  Can be NULL to
2107 **			use entire pvp.
2108 **		buf -- buffer to build the string into.
2109 **		sz -- size of buf.
2110 **		spacesub -- the space separator character; if '\0',
2111 **			use SpaceSub.
2112 **
2113 **	Returns:
2114 **		none.
2115 **
2116 **	Side Effects:
2117 **		Destroys buf.
2118 */
2119 
2120 void
2121 cataddr(pvp, evp, buf, sz, spacesub)
2122 	char **pvp;
2123 	char **evp;
2124 	char *buf;
2125 	register int sz;
2126 	int spacesub;
2127 {
2128 	bool oatomtok = false;
2129 	bool natomtok = false;
2130 	register int i;
2131 	register char *p;
2132 
2133 	if (sz <= 0)
2134 		return;
2135 
2136 	if (spacesub == '\0')
2137 		spacesub = SpaceSub;
2138 
2139 	if (pvp == NULL)
2140 	{
2141 		*buf = '\0';
2142 		return;
2143 	}
2144 	p = buf;
2145 	sz -= 2;
2146 	while (*pvp != NULL && sz > 0)
2147 	{
2148 		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
2149 		if (oatomtok && natomtok)
2150 		{
2151 			*p++ = spacesub;
2152 			if (--sz <= 0)
2153 				break;
2154 		}
2155 		if ((i = sm_strlcpy(p, *pvp, sz)) >= sz)
2156 			break;
2157 		oatomtok = natomtok;
2158 		p += i;
2159 		sz -= i;
2160 		if (pvp++ == evp)
2161 			break;
2162 	}
2163 #if _FFR_CATCH_LONG_STRINGS
2164 	/* Don't silently truncate long strings */
2165 	if (*pvp != NULL)
2166 		syserr("cataddr: string too long");
2167 #endif /* _FFR_CATCH_LONG_STRINGS */
2168 	*p = '\0';
2169 }
2170 /*
2171 **  SAMEADDR -- Determine if two addresses are the same
2172 **
2173 **	This is not just a straight comparison -- if the mailer doesn't
2174 **	care about the host we just ignore it, etc.
2175 **
2176 **	Parameters:
2177 **		a, b -- pointers to the internal forms to compare.
2178 **
2179 **	Returns:
2180 **		true -- they represent the same mailbox.
2181 **		false -- they don't.
2182 **
2183 **	Side Effects:
2184 **		none.
2185 */
2186 
2187 bool
2188 sameaddr(a, b)
2189 	register ADDRESS *a;
2190 	register ADDRESS *b;
2191 {
2192 	register ADDRESS *ca, *cb;
2193 
2194 	/* if they don't have the same mailer, forget it */
2195 	if (a->q_mailer != b->q_mailer)
2196 		return false;
2197 
2198 	/* if the user isn't the same, we can drop out */
2199 	if (strcmp(a->q_user, b->q_user) != 0)
2200 		return false;
2201 
2202 	/* if we have good uids for both but they differ, these are different */
2203 	if (a->q_mailer == ProgMailer)
2204 	{
2205 		ca = getctladdr(a);
2206 		cb = getctladdr(b);
2207 		if (ca != NULL && cb != NULL &&
2208 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
2209 		    ca->q_uid != cb->q_uid)
2210 			return false;
2211 	}
2212 
2213 	/* otherwise compare hosts (but be careful for NULL ptrs) */
2214 	if (a->q_host == b->q_host)
2215 	{
2216 		/* probably both null pointers */
2217 		return true;
2218 	}
2219 	if (a->q_host == NULL || b->q_host == NULL)
2220 	{
2221 		/* only one is a null pointer */
2222 		return false;
2223 	}
2224 	if (strcmp(a->q_host, b->q_host) != 0)
2225 		return false;
2226 
2227 	return true;
2228 }
2229 /*
2230 **  PRINTADDR -- print address (for debugging)
2231 **
2232 **	Parameters:
2233 **		a -- the address to print
2234 **		follow -- follow the q_next chain.
2235 **
2236 **	Returns:
2237 **		none.
2238 **
2239 **	Side Effects:
2240 **		none.
2241 */
2242 
2243 struct qflags
2244 {
2245 	char		*qf_name;
2246 	unsigned long	qf_bit;
2247 };
2248 
2249 static struct qflags	AddressFlags[] =
2250 {
2251 	{ "QGOODUID",		QGOODUID	},
2252 	{ "QPRIMARY",		QPRIMARY	},
2253 	{ "QNOTREMOTE",		QNOTREMOTE	},
2254 	{ "QSELFREF",		QSELFREF	},
2255 	{ "QBOGUSSHELL",	QBOGUSSHELL	},
2256 	{ "QUNSAFEADDR",	QUNSAFEADDR	},
2257 	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
2258 	{ "QPINGONFAILURE",	QPINGONFAILURE	},
2259 	{ "QPINGONDELAY",	QPINGONDELAY	},
2260 	{ "QHASNOTIFY",		QHASNOTIFY	},
2261 	{ "QRELAYED",		QRELAYED	},
2262 	{ "QEXPANDED",		QEXPANDED	},
2263 	{ "QDELIVERED",		QDELIVERED	},
2264 	{ "QDELAYED",		QDELAYED	},
2265 	{ "QTHISPASS",		QTHISPASS	},
2266 	{ "QRCPTOK",		QRCPTOK		},
2267 	{ NULL,			0		}
2268 };
2269 
2270 void
2271 printaddr(a, follow)
2272 	register ADDRESS *a;
2273 	bool follow;
2274 {
2275 	register MAILER *m;
2276 	MAILER pseudomailer;
2277 	register struct qflags *qfp;
2278 	bool firstone;
2279 
2280 	if (a == NULL)
2281 	{
2282 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "[NULL]\n");
2283 		return;
2284 	}
2285 
2286 	while (a != NULL)
2287 	{
2288 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%p=", a);
2289 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
2290 
2291 		/* find the mailer -- carefully */
2292 		m = a->q_mailer;
2293 		if (m == NULL)
2294 		{
2295 			m = &pseudomailer;
2296 			m->m_mno = -1;
2297 			m->m_name = "NULL";
2298 		}
2299 
2300 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2301 				     "%s:\n\tmailer %d (%s), host `%s'\n",
2302 				     a->q_paddr == NULL ? "<null>" : a->q_paddr,
2303 				     m->m_mno, m->m_name,
2304 				     a->q_host == NULL ? "<null>" : a->q_host);
2305 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2306 				     "\tuser `%s', ruser `%s'\n",
2307 				     a->q_user,
2308 				     a->q_ruser == NULL ? "<null>" : a->q_ruser);
2309 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tstate=");
2310 		switch (a->q_state)
2311 		{
2312 		  case QS_OK:
2313 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK");
2314 			break;
2315 
2316 		  case QS_DONTSEND:
2317 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2318 					     "DONTSEND");
2319 			break;
2320 
2321 		  case QS_BADADDR:
2322 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2323 					     "BADADDR");
2324 			break;
2325 
2326 		  case QS_QUEUEUP:
2327 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2328 					     "QUEUEUP");
2329 			break;
2330 
2331 		  case QS_RETRY:
2332 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "RETRY");
2333 			break;
2334 
2335 		  case QS_SENT:
2336 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "SENT");
2337 			break;
2338 
2339 		  case QS_VERIFIED:
2340 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2341 					     "VERIFIED");
2342 			break;
2343 
2344 		  case QS_EXPANDED:
2345 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2346 					     "EXPANDED");
2347 			break;
2348 
2349 		  case QS_SENDER:
2350 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2351 					     "SENDER");
2352 			break;
2353 
2354 		  case QS_CLONED:
2355 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2356 					     "CLONED");
2357 			break;
2358 
2359 		  case QS_DISCARDED:
2360 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2361 					     "DISCARDED");
2362 			break;
2363 
2364 		  case QS_REPLACED:
2365 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2366 					     "REPLACED");
2367 			break;
2368 
2369 		  case QS_REMOVED:
2370 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2371 					     "REMOVED");
2372 			break;
2373 
2374 		  case QS_DUPLICATE:
2375 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2376 					     "DUPLICATE");
2377 			break;
2378 
2379 		  case QS_INCLUDED:
2380 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2381 					     "INCLUDED");
2382 			break;
2383 
2384 		  default:
2385 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2386 					     "%d", a->q_state);
2387 			break;
2388 		}
2389 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2390 				     ", next=%p, alias %p, uid %d, gid %d\n",
2391 				     a->q_next, a->q_alias,
2392 				     (int) a->q_uid, (int) a->q_gid);
2393 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\tflags=%lx<",
2394 				     a->q_flags);
2395 		firstone = true;
2396 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
2397 		{
2398 			if (!bitset(qfp->qf_bit, a->q_flags))
2399 				continue;
2400 			if (!firstone)
2401 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2402 						     ",");
2403 			firstone = false;
2404 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
2405 					     qfp->qf_name);
2406 		}
2407 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, ">\n");
2408 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2409 				     "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
2410 				     a->q_owner == NULL ? "(none)" : a->q_owner,
2411 				     a->q_home == NULL ? "(none)" : a->q_home,
2412 				     a->q_fullname == NULL ? "(none)" : a->q_fullname);
2413 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2414 				     "\torcpt=\"%s\", statmta=%s, status=%s\n",
2415 				     a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
2416 				     a->q_statmta == NULL ? "(none)" : a->q_statmta,
2417 				     a->q_status == NULL ? "(none)" : a->q_status);
2418 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2419 				     "\tfinalrcpt=\"%s\"\n",
2420 				     a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
2421 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2422 				     "\trstatus=\"%s\"\n",
2423 				     a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2424 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2425 				     "\tstatdate=%s\n",
2426 				     a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
2427 
2428 		if (!follow)
2429 			return;
2430 		a = a->q_next;
2431 	}
2432 }
2433 /*
2434 **  EMPTYADDR -- return true if this address is empty (``<>'')
2435 **
2436 **	Parameters:
2437 **		a -- pointer to the address
2438 **
2439 **	Returns:
2440 **		true -- if this address is "empty" (i.e., no one should
2441 **			ever generate replies to it.
2442 **		false -- if it is a "regular" (read: replyable) address.
2443 */
2444 
2445 bool
2446 emptyaddr(a)
2447 	register ADDRESS *a;
2448 {
2449 	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2450 	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2451 }
2452 /*
2453 **  REMOTENAME -- return the name relative to the current mailer
2454 **
2455 **	Parameters:
2456 **		name -- the name to translate.
2457 **		m -- the mailer that we want to do rewriting relative
2458 **			to.
2459 **		flags -- fine tune operations.
2460 **		pstat -- pointer to status word.
2461 **		e -- the current envelope.
2462 **
2463 **	Returns:
2464 **		the text string representing this address relative to
2465 **			the receiving mailer.
2466 **
2467 **	Side Effects:
2468 **		none.
2469 **
2470 **	Warnings:
2471 **		The text string returned is tucked away locally;
2472 **			copy it if you intend to save it.
2473 */
2474 
2475 char *
2476 remotename(name, m, flags, pstat, e)
2477 	char *name;
2478 	struct mailer *m;
2479 	int flags;
2480 	int *pstat;
2481 	register ENVELOPE *e;
2482 {
2483 	register char **pvp;
2484 	char *SM_NONVOLATILE fancy;
2485 	char *oldg;
2486 	int rwset;
2487 	static char buf[MAXNAME + 1];
2488 	char lbuf[MAXNAME + 1];
2489 	char pvpbuf[PSBUFSIZE];
2490 	char addrtype[4];
2491 
2492 	if (tTd(12, 1))
2493 		sm_dprintf("remotename(%s)\n", name);
2494 
2495 	/* don't do anything if we are tagging it as special */
2496 	if (bitset(RF_SENDERADDR, flags))
2497 	{
2498 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2499 						     : m->m_se_rwset;
2500 		addrtype[2] = 's';
2501 	}
2502 	else
2503 	{
2504 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2505 						     : m->m_re_rwset;
2506 		addrtype[2] = 'r';
2507 	}
2508 	if (rwset < 0)
2509 		return name;
2510 	addrtype[1] = ' ';
2511 	addrtype[3] = '\0';
2512 	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
2513 	macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
2514 
2515 	/*
2516 	**  Do a heuristic crack of this name to extract any comment info.
2517 	**	This will leave the name as a comment and a $g macro.
2518 	*/
2519 
2520 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2521 		fancy = "\201g";
2522 	else
2523 		fancy = crackaddr(name, e);
2524 
2525 	/*
2526 	**  Turn the name into canonical form.
2527 	**	Normally this will be RFC 822 style, i.e., "user@domain".
2528 	**	If this only resolves to "user", and the "C" flag is
2529 	**	specified in the sending mailer, then the sender's
2530 	**	domain will be appended.
2531 	*/
2532 
2533 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2534 	if (pvp == NULL)
2535 		return name;
2536 	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2537 		*pstat = EX_TEMPFAIL;
2538 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2539 	{
2540 		/* append from domain to this address */
2541 		register char **pxp = pvp;
2542 		int l = MAXATOM;	/* size of buffer for pvp */
2543 
2544 		/* see if there is an "@domain" in the current name */
2545 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2546 		{
2547 			pxp++;
2548 			--l;
2549 		}
2550 		if (*pxp == NULL)
2551 		{
2552 			/* no.... append the "@domain" from the sender */
2553 			register char **qxq = e->e_fromdomain;
2554 
2555 			while ((*pxp++ = *qxq++) != NULL)
2556 			{
2557 				if (--l <= 0)
2558 				{
2559 					*--pxp = NULL;
2560 					usrerr("553 5.1.0 remotename: too many tokens");
2561 					*pstat = EX_UNAVAILABLE;
2562 					break;
2563 				}
2564 			}
2565 			if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2566 				*pstat = EX_TEMPFAIL;
2567 		}
2568 	}
2569 
2570 	/*
2571 	**  Do more specific rewriting.
2572 	**	Rewrite using ruleset 1 or 2 depending on whether this is
2573 	**		a sender address or not.
2574 	**	Then run it through any receiving-mailer-specific rulesets.
2575 	*/
2576 
2577 	if (bitset(RF_SENDERADDR, flags))
2578 	{
2579 		if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
2580 			*pstat = EX_TEMPFAIL;
2581 	}
2582 	else
2583 	{
2584 		if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
2585 			*pstat = EX_TEMPFAIL;
2586 	}
2587 	if (rwset > 0)
2588 	{
2589 		if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
2590 			*pstat = EX_TEMPFAIL;
2591 	}
2592 
2593 	/*
2594 	**  Do any final sanitation the address may require.
2595 	**	This will normally be used to turn internal forms
2596 	**	(e.g., user@host.LOCAL) into external form.  This
2597 	**	may be used as a default to the above rules.
2598 	*/
2599 
2600 	if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
2601 		*pstat = EX_TEMPFAIL;
2602 
2603 	/*
2604 	**  Now restore the comment information we had at the beginning.
2605 	*/
2606 
2607 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
2608 	oldg = macget(&e->e_macro, 'g');
2609 	macset(&e->e_macro, 'g', lbuf);
2610 
2611 	SM_TRY
2612 		/* need to make sure route-addrs have <angle brackets> */
2613 		if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2614 			expand("<\201g>", buf, sizeof buf, e);
2615 		else
2616 			expand(fancy, buf, sizeof buf, e);
2617 	SM_FINALLY
2618 		macset(&e->e_macro, 'g', oldg);
2619 	SM_END_TRY
2620 
2621 	if (tTd(12, 1))
2622 		sm_dprintf("remotename => `%s'\n", buf);
2623 	return buf;
2624 }
2625 /*
2626 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2627 **
2628 **	Parameters:
2629 **		a -- the address to map (but just the user name part).
2630 **		sendq -- the sendq in which to install any replacement
2631 **			addresses.
2632 **		aliaslevel -- the alias nesting depth.
2633 **		e -- the envelope.
2634 **
2635 **	Returns:
2636 **		none.
2637 */
2638 
2639 #define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2640 			 Q_PINGFLAGS|QHASNOTIFY|\
2641 			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
2642 			 QBYTRACE|QBYNDELAY|QBYNRELAY)
2643 
2644 void
2645 maplocaluser(a, sendq, aliaslevel, e)
2646 	register ADDRESS *a;
2647 	ADDRESS **sendq;
2648 	int aliaslevel;
2649 	ENVELOPE *e;
2650 {
2651 	register char **pvp;
2652 	register ADDRESS *SM_NONVOLATILE a1 = NULL;
2653 	auto char *delimptr;
2654 	char pvpbuf[PSBUFSIZE];
2655 
2656 	if (tTd(29, 1))
2657 	{
2658 		sm_dprintf("maplocaluser: ");
2659 		printaddr(a, false);
2660 	}
2661 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
2662 	if (pvp == NULL)
2663 	{
2664 		if (tTd(29, 9))
2665 			sm_dprintf("maplocaluser: cannot prescan %s\n",
2666 				a->q_user);
2667 		return;
2668 	}
2669 
2670 	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2671 	macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
2672 	macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
2673 
2674 	macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
2675 	if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
2676 	{
2677 		if (tTd(29, 9))
2678 			sm_dprintf("maplocaluser: rewrite tempfail\n");
2679 		a->q_state = QS_QUEUEUP;
2680 		a->q_status = "4.4.3";
2681 		return;
2682 	}
2683 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2684 	{
2685 		if (tTd(29, 9))
2686 			sm_dprintf("maplocaluser: doesn't resolve\n");
2687 		return;
2688 	}
2689 
2690 	SM_TRY
2691 		a1 = buildaddr(pvp, NULL, 0, e);
2692 	SM_EXCEPT(exc, "E:mta.quickabort")
2693 
2694 		/*
2695 		**  mark address as bad, S5 returned an error
2696 		**	and we gave that back to the SMTP client.
2697 		*/
2698 
2699 		a->q_state = QS_DONTSEND;
2700 		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
2701 	SM_END_TRY
2702 
2703 	/* if non-null, mailer destination specified -- has it changed? */
2704 	if (a1 == NULL || sameaddr(a, a1))
2705 	{
2706 		if (tTd(29, 9))
2707 			sm_dprintf("maplocaluser: address unchanged\n");
2708 		return;
2709 	}
2710 
2711 	/* make new address take on flags and print attributes of old */
2712 	a1->q_flags &= ~Q_COPYFLAGS;
2713 	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2714 	a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
2715 	a1->q_finalrcpt = a->q_finalrcpt;
2716 	a1->q_orcpt = a->q_orcpt;
2717 
2718 	/* mark old address as dead; insert new address */
2719 	a->q_state = QS_REPLACED;
2720 	if (tTd(29, 5))
2721 	{
2722 		sm_dprintf("maplocaluser: QS_REPLACED ");
2723 		printaddr(a, false);
2724 	}
2725 	a1->q_alias = a;
2726 	allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
2727 	(void) recipient(a1, sendq, aliaslevel, e);
2728 }
2729 /*
2730 **  DEQUOTE_INIT -- initialize dequote map
2731 **
2732 **	Parameters:
2733 **		map -- the internal map structure.
2734 **		args -- arguments.
2735 **
2736 **	Returns:
2737 **		true.
2738 */
2739 
2740 bool
2741 dequote_init(map, args)
2742 	MAP *map;
2743 	char *args;
2744 {
2745 	register char *p = args;
2746 
2747 	/* there is no check whether there is really an argument */
2748 	map->map_mflags |= MF_KEEPQUOTES;
2749 	for (;;)
2750 	{
2751 		while (isascii(*p) && isspace(*p))
2752 			p++;
2753 		if (*p != '-')
2754 			break;
2755 		switch (*++p)
2756 		{
2757 		  case 'a':
2758 			map->map_app = ++p;
2759 			break;
2760 
2761 		  case 'D':
2762 			map->map_mflags |= MF_DEFER;
2763 			break;
2764 
2765 		  case 'S':
2766 		  case 's':
2767 			map->map_spacesub = *++p;
2768 			break;
2769 		}
2770 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2771 			p++;
2772 		if (*p != '\0')
2773 			*p = '\0';
2774 	}
2775 	if (map->map_app != NULL)
2776 		map->map_app = newstr(map->map_app);
2777 
2778 	return true;
2779 }
2780 /*
2781 **  DEQUOTE_MAP -- unquote an address
2782 **
2783 **	Parameters:
2784 **		map -- the internal map structure (ignored).
2785 **		name -- the name to dequote.
2786 **		av -- arguments (ignored).
2787 **		statp -- pointer to status out-parameter.
2788 **
2789 **	Returns:
2790 **		NULL -- if there were no quotes, or if the resulting
2791 **			unquoted buffer would not be acceptable to prescan.
2792 **		else -- The dequoted buffer.
2793 */
2794 
2795 /* ARGSUSED2 */
2796 char *
2797 dequote_map(map, name, av, statp)
2798 	MAP *map;
2799 	char *name;
2800 	char **av;
2801 	int *statp;
2802 {
2803 	register char *p;
2804 	register char *q;
2805 	register char c;
2806 	int anglecnt = 0;
2807 	int cmntcnt = 0;
2808 	int quotecnt = 0;
2809 	int spacecnt = 0;
2810 	bool quotemode = false;
2811 	bool bslashmode = false;
2812 	char spacesub = map->map_spacesub;
2813 
2814 	for (p = q = name; (c = *p++) != '\0'; )
2815 	{
2816 		if (bslashmode)
2817 		{
2818 			bslashmode = false;
2819 			*q++ = c;
2820 			continue;
2821 		}
2822 
2823 		if (c == ' ' && spacesub != '\0')
2824 			c = spacesub;
2825 
2826 		switch (c)
2827 		{
2828 		  case '\\':
2829 			bslashmode = true;
2830 			break;
2831 
2832 		  case '(':
2833 			cmntcnt++;
2834 			break;
2835 
2836 		  case ')':
2837 			if (cmntcnt-- <= 0)
2838 				return NULL;
2839 			break;
2840 
2841 		  case ' ':
2842 		  case '\t':
2843 			spacecnt++;
2844 			break;
2845 		}
2846 
2847 		if (cmntcnt > 0)
2848 		{
2849 			*q++ = c;
2850 			continue;
2851 		}
2852 
2853 		switch (c)
2854 		{
2855 		  case '"':
2856 			quotemode = !quotemode;
2857 			quotecnt++;
2858 			continue;
2859 
2860 		  case '<':
2861 			anglecnt++;
2862 			break;
2863 
2864 		  case '>':
2865 			if (anglecnt-- <= 0)
2866 				return NULL;
2867 			break;
2868 		}
2869 		*q++ = c;
2870 	}
2871 
2872 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2873 	    quotemode || quotecnt <= 0 || spacecnt != 0)
2874 		return NULL;
2875 	*q++ = '\0';
2876 	return map_rewrite(map, name, strlen(name), NULL);
2877 }
2878 /*
2879 **  RSCHECK -- check string(s) for validity using rewriting sets
2880 **
2881 **	Parameters:
2882 **		rwset -- the rewriting set to use.
2883 **		p1 -- the first string to check.
2884 **		p2 -- the second string to check -- may be null.
2885 **		e -- the current envelope.
2886 **		flags -- control some behavior, see RSF_ in sendmail.h
2887 **		logl -- logging level.
2888 **		host -- NULL or relay host.
2889 **		logid -- id for sm_syslog.
2890 **
2891 **	Returns:
2892 **		EX_OK -- if the rwset doesn't resolve to $#error
2893 **		else -- the failure status (message printed)
2894 */
2895 
2896 int
2897 rscheck(rwset, p1, p2, e, flags, logl, host, logid)
2898 	char *rwset;
2899 	char *p1;
2900 	char *p2;
2901 	ENVELOPE *e;
2902 	int flags;
2903 	int logl;
2904 	char *host;
2905 	char *logid;
2906 {
2907 	char *volatile buf;
2908 	int bufsize;
2909 	int saveexitstat;
2910 	int volatile rstat = EX_OK;
2911 	char **pvp;
2912 	int rsno;
2913 	bool volatile discard = false;
2914 	auto ADDRESS a1;
2915 	bool saveQuickAbort = QuickAbort;
2916 	bool saveSuprErrs = SuprErrs;
2917 #if _FFR_QUARANTINE
2918 	bool quarantine = false;
2919 	char ubuf[BUFSIZ * 2];
2920 #endif /* _FFR_QUARANTINE */
2921 	char buf0[MAXLINE];
2922 	char pvpbuf[PSBUFSIZE];
2923 	extern char MsgBuf[];
2924 
2925 	if (tTd(48, 2))
2926 		sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
2927 			p2 == NULL ? "(NULL)" : p2);
2928 
2929 	rsno = strtorwset(rwset, NULL, ST_FIND);
2930 	if (rsno < 0)
2931 		return EX_OK;
2932 
2933 	if (p2 != NULL)
2934 	{
2935 		bufsize = strlen(p1) + strlen(p2) + 2;
2936 		if (bufsize > sizeof buf0)
2937 			buf = sm_malloc_x(bufsize);
2938 		else
2939 		{
2940 			buf = buf0;
2941 			bufsize = sizeof buf0;
2942 		}
2943 		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
2944 	}
2945 	else
2946 	{
2947 		bufsize = strlen(p1) + 1;
2948 		if (bufsize > sizeof buf0)
2949 			buf = sm_malloc_x(bufsize);
2950 		else
2951 		{
2952 			buf = buf0;
2953 			bufsize = sizeof buf0;
2954 		}
2955 		(void) sm_strlcpy(buf, p1, bufsize);
2956 	}
2957 	SM_TRY
2958 	{
2959 		SuprErrs = true;
2960 		QuickAbort = false;
2961 		pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
2962 			      bitset(RSF_RMCOMM, flags) ? NULL : TokTypeNoC);
2963 		SuprErrs = saveSuprErrs;
2964 		if (pvp == NULL)
2965 		{
2966 			if (tTd(48, 2))
2967 				sm_dprintf("rscheck: cannot prescan input\n");
2968 	/*
2969 			syserr("rscheck: cannot prescan input: \"%s\"",
2970 				shortenstring(buf, MAXSHORTSTR));
2971 			rstat = EX_DATAERR;
2972 	*/
2973 			goto finis;
2974 		}
2975 		if (bitset(RSF_UNSTRUCTURED, flags))
2976 			SuprErrs = true;
2977 		(void) REWRITE(pvp, rsno, e);
2978 		if (bitset(RSF_UNSTRUCTURED, flags))
2979 			SuprErrs = saveSuprErrs;
2980 		if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
2981 		    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
2982 				       strcmp(pvp[1], "discard") != 0))
2983 		{
2984 			goto finis;
2985 		}
2986 
2987 		if (strcmp(pvp[1], "discard") == 0)
2988 		{
2989 			if (tTd(48, 2))
2990 				sm_dprintf("rscheck: discard mailer selected\n");
2991 			e->e_flags |= EF_DISCARD;
2992 			discard = true;
2993 		}
2994 #if _FFR_QUARANTINE
2995 		else if (strcmp(pvp[1], "error") == 0 &&
2996 			 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
2997 			 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
2998 		{
2999 			if (pvp[4] == NULL ||
3000 			    (pvp[4][0] & 0377) != CANONUSER ||
3001 			    pvp[5] == NULL)
3002 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3003 								 rwset);
3004 			else
3005 			{
3006 				cataddr(&(pvp[5]), NULL, ubuf,
3007 					sizeof ubuf, ' ');
3008 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3009 								 ubuf);
3010 			}
3011 			macdefine(&e->e_macro, A_PERM,
3012 				  macid("{quarantine}"), e->e_quarmsg);
3013 			quarantine = true;
3014 		}
3015 #endif /* _FFR_QUARANTINE */
3016 		else
3017 		{
3018 			int savelogusrerrs = LogUsrErrs;
3019 			static bool logged = false;
3020 
3021 			/* got an error -- process it */
3022 			saveexitstat = ExitStat;
3023 			LogUsrErrs = false;
3024 			(void) buildaddr(pvp, &a1, 0, e);
3025 			LogUsrErrs = savelogusrerrs;
3026 			rstat = ExitStat;
3027 			ExitStat = saveexitstat;
3028 			if (!logged)
3029 			{
3030 				if (bitset(RSF_COUNT, flags))
3031 					markstats(e, &a1, STATS_REJECT);
3032 				logged = true;
3033 			}
3034 		}
3035 
3036 		if (LogLevel > logl)
3037 		{
3038 			char *relay;
3039 			char *p;
3040 			char lbuf[MAXLINE];
3041 
3042 			p = lbuf;
3043 			if (p2 != NULL)
3044 			{
3045 				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
3046 					", arg2=%s",
3047 					p2);
3048 				p += strlen(p);
3049 			}
3050 
3051 			if (host != NULL)
3052 				relay = host;
3053 			else
3054 				relay = macvalue('_', e);
3055 			if (relay != NULL)
3056 			{
3057 				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
3058 					", relay=%s", relay);
3059 				p += strlen(p);
3060 			}
3061 			*p = '\0';
3062 			if (discard)
3063 				sm_syslog(LOG_NOTICE, logid,
3064 					  "ruleset=%s, arg1=%s%s, discard",
3065 					  rwset, p1, lbuf);
3066 #if _FFR_QUARANTINE
3067 			else if (quarantine)
3068 				sm_syslog(LOG_NOTICE, logid,
3069 					  "ruleset=%s, arg1=%s%s, quarantine=%s",
3070 					  rwset, p1, lbuf, ubuf);
3071 #endif /* _FFR_QUARANTINE */
3072 			else
3073 				sm_syslog(LOG_NOTICE, logid,
3074 					  "ruleset=%s, arg1=%s%s, reject=%s",
3075 					  rwset, p1, lbuf, MsgBuf);
3076 		}
3077 
3078 	 finis: ;
3079 	}
3080 	SM_FINALLY
3081 	{
3082 		/* clean up */
3083 		if (buf != buf0)
3084 			sm_free(buf);
3085 		QuickAbort = saveQuickAbort;
3086 	}
3087 	SM_END_TRY
3088 
3089 	setstat(rstat);
3090 
3091 	/* rulesets don't set errno */
3092 	errno = 0;
3093 	if (rstat != EX_OK && QuickAbort)
3094 		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
3095 	return rstat;
3096 }
3097 /*
3098 **  RSCAP -- call rewriting set to return capabilities
3099 **
3100 **	Parameters:
3101 **		rwset -- the rewriting set to use.
3102 **		p1 -- the first string to check.
3103 **		p2 -- the second string to check -- may be null.
3104 **		e -- the current envelope.
3105 **		pvp -- pointer to token vector.
3106 **		pvpbuf -- buffer space.
3107 **
3108 **	Returns:
3109 **		EX_UNAVAILABLE -- ruleset doesn't exist.
3110 **		EX_DATAERR -- prescan() failed.
3111 **		EX_OK -- rewrite() was successful.
3112 **		else -- return status from rewrite().
3113 */
3114 
3115 int
3116 rscap(rwset, p1, p2, e, pvp, pvpbuf, size)
3117 	char *rwset;
3118 	char *p1;
3119 	char *p2;
3120 	ENVELOPE *e;
3121 	char ***pvp;
3122 	char *pvpbuf;
3123 	int size;
3124 {
3125 	char *volatile buf;
3126 	int bufsize;
3127 	int volatile rstat = EX_OK;
3128 	int rsno;
3129 	bool saveQuickAbort = QuickAbort;
3130 	bool saveSuprErrs = SuprErrs;
3131 	char buf0[MAXLINE];
3132 	extern char MsgBuf[];
3133 
3134 	if (tTd(48, 2))
3135 		sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
3136 			p2 == NULL ? "(NULL)" : p2);
3137 
3138 	if (pvp != NULL)
3139 		*pvp = NULL;
3140 	rsno = strtorwset(rwset, NULL, ST_FIND);
3141 	if (rsno < 0)
3142 		return EX_UNAVAILABLE;
3143 
3144 	if (p2 != NULL)
3145 	{
3146 		bufsize = strlen(p1) + strlen(p2) + 2;
3147 		if (bufsize > sizeof buf0)
3148 			buf = sm_malloc_x(bufsize);
3149 		else
3150 		{
3151 			buf = buf0;
3152 			bufsize = sizeof buf0;
3153 		}
3154 		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
3155 	}
3156 	else
3157 	{
3158 		bufsize = strlen(p1) + 1;
3159 		if (bufsize > sizeof buf0)
3160 			buf = sm_malloc_x(bufsize);
3161 		else
3162 		{
3163 			buf = buf0;
3164 			bufsize = sizeof buf0;
3165 		}
3166 		(void) sm_strlcpy(buf, p1, bufsize);
3167 	}
3168 	SM_TRY
3169 	{
3170 		SuprErrs = true;
3171 		QuickAbort = false;
3172 		*pvp = prescan(buf, '\0', pvpbuf, size, NULL, NULL);
3173 		if (*pvp != NULL)
3174 			rstat = REWRITE(*pvp, rsno, e);
3175 		else
3176 		{
3177 			if (tTd(48, 2))
3178 				sm_dprintf("rscap: cannot prescan input\n");
3179 			rstat = EX_DATAERR;
3180 		}
3181 	}
3182 	SM_FINALLY
3183 	{
3184 		/* clean up */
3185 		if (buf != buf0)
3186 			sm_free(buf);
3187 		SuprErrs = saveSuprErrs;
3188 		QuickAbort = saveQuickAbort;
3189 
3190 		/* prevent information leak, this may contain rewrite error */
3191 		MsgBuf[0] = '\0';
3192 	}
3193 	SM_END_TRY
3194 	return rstat;
3195 }
3196