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