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.400 2006/12/21 00:24:06 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: queuing 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 			STAB *map;
1466 			char *mapname;
1467 			char **key_rvp;
1468 			char **arg_rvp;
1469 			char **default_rvp;
1470 			char cbuf[MAXKEY];
1471 			char *pvpb1[MAXATOM + 1];
1472 			char *argvect[MAX_MAP_ARGS];
1473 			char pvpbuf[PSBUFSIZE];
1474 			char *nullpvp[1];
1475 
1476 			hbrvp = rvp;
1477 			if ((rvp[0][0] & 0377) == HOSTBEGIN)
1478 			{
1479 				endtoken = HOSTEND;
1480 				mapname = "host";
1481 			}
1482 			else if ((rvp[0][0] & 0377) == LOOKUPBEGIN)
1483 			{
1484 				endtoken = LOOKUPEND;
1485 				mapname = *++rvp;
1486 				if (mapname == NULL)
1487 				{
1488 					syserr("554 5.3.0 rewrite: missing mapname");
1489 					/* NOTREACHED */
1490 					SM_ASSERT(0);
1491 				}
1492 			}
1493 			else
1494 				continue;
1495 
1496 			/*
1497 			**  Got a hostname/keyword lookup.
1498 			**
1499 			**	This could be optimized fairly easily.
1500 			*/
1501 
1502 			map = stab(mapname, ST_MAP, ST_FIND);
1503 			if (map == NULL)
1504 				syserr("554 5.3.0 rewrite: map %s not found",
1505 					mapname);
1506 
1507 			/* extract the match part */
1508 			key_rvp = ++rvp;
1509 			if (key_rvp == NULL)
1510 			{
1511 				syserr("554 5.3.0 rewrite: missing key for map %s",
1512 					mapname);
1513 				/* NOTREACHED */
1514 				SM_ASSERT(0);
1515 			}
1516 			default_rvp = NULL;
1517 			arg_rvp = argvect;
1518 			xpvp = NULL;
1519 			replac = pvpbuf;
1520 			while (*rvp != NULL && ((rvp[0][0] & 0377) != endtoken))
1521 			{
1522 				int nodetype = rvp[0][0] & 0377;
1523 
1524 				if (nodetype != CANONHOST &&
1525 				    nodetype != CANONUSER)
1526 				{
1527 					rvp++;
1528 					continue;
1529 				}
1530 
1531 				*rvp++ = NULL;
1532 
1533 				if (xpvp != NULL)
1534 				{
1535 					cataddr(xpvp, NULL, replac,
1536 						&pvpbuf[sizeof(pvpbuf)] - replac,
1537 						'\0', false);
1538 					if (arg_rvp <
1539 					    &argvect[MAX_MAP_ARGS - 1])
1540 						*++arg_rvp = replac;
1541 					replac += strlen(replac) + 1;
1542 					xpvp = NULL;
1543 				}
1544 				switch (nodetype)
1545 				{
1546 				  case CANONHOST:
1547 					xpvp = rvp;
1548 					break;
1549 
1550 				  case CANONUSER:
1551 					default_rvp = rvp;
1552 					break;
1553 				}
1554 			}
1555 			if (*rvp != NULL)
1556 				*rvp++ = NULL;
1557 			if (xpvp != NULL)
1558 			{
1559 				cataddr(xpvp, NULL, replac,
1560 					&pvpbuf[sizeof(pvpbuf)] - replac,
1561 					'\0', false);
1562 				if (arg_rvp < &argvect[MAX_MAP_ARGS - 1])
1563 					*++arg_rvp = replac;
1564 			}
1565 			if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1])
1566 				argvect[MAX_MAP_ARGS - 1] = NULL;
1567 			else
1568 				*++arg_rvp = NULL;
1569 
1570 			/* save the remainder of the input string */
1571 			trsize = (avp - rvp + 1) * sizeof(*rvp);
1572 			memmove((char *) pvpb1, (char *) rvp, trsize);
1573 
1574 			/* look it up */
1575 			cataddr(key_rvp, NULL, cbuf, sizeof(cbuf),
1576 				map == NULL ? '\0' : map->s_map.map_spacesub,
1577 				true);
1578 			argvect[0] = cbuf;
1579 			replac = map_lookup(map, cbuf, argvect, &rstat, e);
1580 
1581 			/* if no replacement, use default */
1582 			if (replac == NULL && default_rvp != NULL)
1583 			{
1584 				/* create the default */
1585 				cataddr(default_rvp, NULL, cbuf, sizeof(cbuf),
1586 					'\0', false);
1587 				replac = cbuf;
1588 			}
1589 
1590 			if (replac == NULL)
1591 			{
1592 				xpvp = key_rvp;
1593 			}
1594 			else if (*replac == '\0')
1595 			{
1596 				/* null replacement */
1597 				nullpvp[0] = NULL;
1598 				xpvp = nullpvp;
1599 			}
1600 			else
1601 			{
1602 				/* scan the new replacement */
1603 				xpvp = prescan(replac, '\0', pvpbuf,
1604 					       sizeof(pvpbuf), NULL, NULL,
1605 					       false);
1606 				if (xpvp == NULL)
1607 				{
1608 					/* prescan already printed error */
1609 					return EX_DATAERR;
1610 				}
1611 			}
1612 
1613 			/* append it to the token list */
1614 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
1615 			{
1616 				*avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
1617 				if (avp >= &npvp[maxatom])
1618 					goto toolong;
1619 			}
1620 
1621 			/* restore the old trailing information */
1622 			rvp = avp - 1;
1623 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1624 				if (avp >= &npvp[maxatom])
1625 					goto toolong;
1626 		}
1627 
1628 		/*
1629 		**  Check for subroutine calls.
1630 		*/
1631 
1632 		status = callsubr(npvp, reclevel, e);
1633 		if (rstat == EX_OK || status == EX_TEMPFAIL)
1634 			rstat = status;
1635 
1636 		/* copy vector back into original space. */
1637 		for (avp = npvp; *avp++ != NULL;)
1638 			continue;
1639 		memmove((char *) pvp, (char *) npvp,
1640 		      (int) (avp - npvp) * sizeof(*avp));
1641 
1642 		if (tTd(21, 4))
1643 		{
1644 			sm_dprintf("rewritten as:");
1645 			printav(sm_debug_file(), pvp);
1646 		}
1647 	}
1648 
1649 	if (OpMode == MD_TEST)
1650 	{
1651 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1652 				     "%s%-16.16s returns:", prefix, rulename);
1653 		printav(smioout, pvp);
1654 	}
1655 	else if (tTd(21, 1))
1656 	{
1657 		sm_dprintf("%s%-16.16s returns:", prefix, rulename);
1658 		printav(sm_debug_file(), pvp);
1659 	}
1660 	return rstat;
1661 }
1662 /*
1663 **  CALLSUBR -- call subroutines in rewrite vector
1664 **
1665 **	Parameters:
1666 **		pvp -- pointer to token vector.
1667 **		reclevel -- the current recursion level.
1668 **		e -- the current envelope.
1669 **
1670 **	Returns:
1671 **		The status from the subroutine call.
1672 **
1673 **	Side Effects:
1674 **		pvp is modified.
1675 */
1676 
1677 static int
1678 callsubr(pvp, reclevel, e)
1679 	char **pvp;
1680 	int reclevel;
1681 	ENVELOPE *e;
1682 {
1683 	char **avp;
1684 	register int i;
1685 	int subr, j;
1686 	int nsubr;
1687 	int status;
1688 	int rstat = EX_OK;
1689 #define MAX_SUBR	16
1690 	int subrnumber[MAX_SUBR];
1691 	int subrindex[MAX_SUBR];
1692 
1693 	nsubr = 0;
1694 
1695 	/*
1696 	**  Look for subroutine calls in pvp, collect them into subr*[]
1697 	**  We will perform the calls in the next loop, because we will
1698 	**  call the "last" subroutine first to avoid recursive calls
1699 	**  and too much copying.
1700 	*/
1701 
1702 	for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
1703 	{
1704 		if ((avp[0][0] & 0377) == CALLSUBR && avp[1] != NULL)
1705 		{
1706 			stripquotes(avp[1]);
1707 			subr = strtorwset(avp[1], NULL, ST_FIND);
1708 			if (subr < 0)
1709 			{
1710 				syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
1711 				return EX_CONFIG;
1712 			}
1713 
1714 			/*
1715 			**  XXX instead of doing this we could optimize
1716 			**  the rules after reading them: just remove
1717 			**  calls to empty rulesets
1718 			*/
1719 
1720 			/* subroutine is an empty ruleset?  don't call it */
1721 			if (RewriteRules[subr] == NULL)
1722 			{
1723 				if (tTd(21, 3))
1724 					sm_dprintf("-----skip subr %s (%d)\n",
1725 						avp[1], subr);
1726 				for (i = 2; avp[i] != NULL; i++)
1727 					avp[i - 2] = avp[i];
1728 				avp[i - 2] = NULL;
1729 				continue;
1730 			}
1731 			if (++nsubr >= MAX_SUBR)
1732 			{
1733 				syserr("554 5.3.0 Too many subroutine calls (%d max)",
1734 					MAX_SUBR);
1735 				return EX_CONFIG;
1736 			}
1737 			subrnumber[nsubr] = subr;
1738 			subrindex[nsubr] = j;
1739 		}
1740 	}
1741 
1742 	/*
1743 	**  Perform the actual subroutines calls, "last" one first, i.e.,
1744 	**  go from the right to the left through all calls,
1745 	**  do the rewriting in place.
1746 	*/
1747 
1748 	for (; nsubr > 0; nsubr--)
1749 	{
1750 		subr = subrnumber[nsubr];
1751 		avp = pvp + subrindex[nsubr];
1752 
1753 		/* remove the subroutine call and name */
1754 		for (i = 2; avp[i] != NULL; i++)
1755 			avp[i - 2] = avp[i];
1756 		avp[i - 2] = NULL;
1757 
1758 		/*
1759 		**  Now we need to call the ruleset specified for
1760 		**  the subroutine. We can do this in place since
1761 		**  we call the "last" subroutine first.
1762 		*/
1763 
1764 		status = rewrite(avp, subr, reclevel, e,
1765 				MAXATOM - subrindex[nsubr]);
1766 		if (status != EX_OK && status != EX_TEMPFAIL)
1767 			return status;
1768 		if (rstat == EX_OK || status == EX_TEMPFAIL)
1769 			rstat = status;
1770 	}
1771 	return rstat;
1772 }
1773 /*
1774 **  MAP_LOOKUP -- do lookup in map
1775 **
1776 **	Parameters:
1777 **		smap -- the map to use for the lookup.
1778 **		key -- the key to look up.
1779 **		argvect -- arguments to pass to the map lookup.
1780 **		pstat -- a pointer to an integer in which to store the
1781 **			status from the lookup.
1782 **		e -- the current envelope.
1783 **
1784 **	Returns:
1785 **		The result of the lookup.
1786 **		NULL -- if there was no data for the given key.
1787 */
1788 
1789 static char *
1790 map_lookup(smap, key, argvect, pstat, e)
1791 	STAB *smap;
1792 	char key[];
1793 	char **argvect;
1794 	int *pstat;
1795 	ENVELOPE *e;
1796 {
1797 	auto int status = EX_OK;
1798 	MAP *map;
1799 	char *replac;
1800 
1801 	if (smap == NULL)
1802 		return NULL;
1803 
1804 	map = &smap->s_map;
1805 	DYNOPENMAP(map);
1806 
1807 	if (e->e_sendmode == SM_DEFER &&
1808 	    bitset(MF_DEFER, map->map_mflags))
1809 	{
1810 		/* don't do any map lookups */
1811 		if (tTd(60, 1))
1812 			sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
1813 				smap->s_name, key);
1814 		*pstat = EX_TEMPFAIL;
1815 		return NULL;
1816 	}
1817 
1818 	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
1819 		stripquotes(key);
1820 
1821 	if (tTd(60, 1))
1822 	{
1823 		sm_dprintf("map_lookup(%s, ", smap->s_name);
1824 		xputs(sm_debug_file(), key);
1825 		if (tTd(60, 5))
1826 		{
1827 			int i;
1828 
1829 			for (i = 0; argvect[i] != NULL; i++)
1830 				sm_dprintf(", %%%d=%s", i, argvect[i]);
1831 		}
1832 		sm_dprintf(") => ");
1833 	}
1834 	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
1835 	if (tTd(60, 1))
1836 		sm_dprintf("%s (%d)\n",
1837 			replac != NULL ? replac : "NOT FOUND",
1838 			status);
1839 
1840 	/* should recover if status == EX_TEMPFAIL */
1841 	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
1842 	{
1843 		*pstat = EX_TEMPFAIL;
1844 		if (tTd(60, 1))
1845 			sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
1846 				smap->s_name, key, errno);
1847 		if (e->e_message == NULL)
1848 		{
1849 			char mbuf[320];
1850 
1851 			(void) sm_snprintf(mbuf, sizeof(mbuf),
1852 				"%.80s map: lookup (%s): deferred",
1853 				smap->s_name,
1854 				shortenstring(key, MAXSHORTSTR));
1855 			e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
1856 		}
1857 	}
1858 	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
1859 	{
1860 		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
1861 		static char *rwbuf = NULL;
1862 		static size_t rwbuflen = 0;
1863 
1864 		if (i > rwbuflen)
1865 		{
1866 			if (rwbuf != NULL)
1867 				sm_free(rwbuf);
1868 			rwbuflen = i;
1869 			rwbuf = (char *) sm_pmalloc_x(rwbuflen);
1870 		}
1871 		(void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
1872 		if (tTd(60, 4))
1873 			sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
1874 				rwbuf);
1875 		return rwbuf;
1876 	}
1877 	return replac;
1878 }
1879 /*
1880 **  INITERRMAILERS -- initialize error and discard mailers
1881 **
1882 **	Parameters:
1883 **		none.
1884 **
1885 **	Returns:
1886 **		none.
1887 **
1888 **	Side Effects:
1889 **		initializes error and discard mailers.
1890 */
1891 
1892 static MAILER discardmailer;
1893 static MAILER errormailer;
1894 static char *discardargv[] = { "DISCARD", NULL };
1895 static char *errorargv[] = { "ERROR", NULL };
1896 
1897 void
1898 initerrmailers()
1899 {
1900 	if (discardmailer.m_name == NULL)
1901 	{
1902 		/* initialize the discard mailer */
1903 		discardmailer.m_name = "*discard*";
1904 		discardmailer.m_mailer = "DISCARD";
1905 		discardmailer.m_argv = discardargv;
1906 	}
1907 	if (errormailer.m_name == NULL)
1908 	{
1909 		/* initialize the bogus mailer */
1910 		errormailer.m_name = "*error*";
1911 		errormailer.m_mailer = "ERROR";
1912 		errormailer.m_argv = errorargv;
1913 	}
1914 }
1915 /*
1916 **  BUILDADDR -- build address from token vector.
1917 **
1918 **	Parameters:
1919 **		tv -- token vector.
1920 **		a -- pointer to address descriptor to fill.
1921 **			If NULL, one will be allocated.
1922 **		flags -- info regarding whether this is a sender or
1923 **			a recipient.
1924 **		e -- the current envelope.
1925 **
1926 **	Returns:
1927 **		NULL if there was an error.
1928 **		'a' otherwise.
1929 **
1930 **	Side Effects:
1931 **		fills in 'a'
1932 */
1933 
1934 static struct errcodes
1935 {
1936 	char	*ec_name;		/* name of error code */
1937 	int	ec_code;		/* numeric code */
1938 } ErrorCodes[] =
1939 {
1940 	{ "usage",		EX_USAGE	},
1941 	{ "nouser",		EX_NOUSER	},
1942 	{ "nohost",		EX_NOHOST	},
1943 	{ "unavailable",	EX_UNAVAILABLE	},
1944 	{ "software",		EX_SOFTWARE	},
1945 	{ "tempfail",		EX_TEMPFAIL	},
1946 	{ "protocol",		EX_PROTOCOL	},
1947 	{ "config",		EX_CONFIG	},
1948 	{ NULL,			EX_UNAVAILABLE	}
1949 };
1950 
1951 static ADDRESS *
1952 buildaddr(tv, a, flags, e)
1953 	register char **tv;
1954 	register ADDRESS *a;
1955 	int flags;
1956 	register ENVELOPE *e;
1957 {
1958 	bool tempfail = false;
1959 	int maxatom;
1960 	struct mailer **mp;
1961 	register struct mailer *m;
1962 	register char *p;
1963 	char *mname;
1964 	char **hostp;
1965 	char hbuf[MAXNAME + 1];
1966 	static char ubuf[MAXNAME + 2];
1967 
1968 	if (tTd(24, 5))
1969 	{
1970 		sm_dprintf("buildaddr, flags=%x, tv=", flags);
1971 		printav(sm_debug_file(), tv);
1972 	}
1973 
1974 	maxatom = MAXATOM;
1975 	if (a == NULL)
1976 		a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
1977 	memset((char *) a, '\0', sizeof(*a));
1978 	hbuf[0] = '\0';
1979 
1980 	/* set up default error return flags */
1981 	a->q_flags |= DefaultNotify;
1982 
1983 	/* figure out what net/mailer to use */
1984 	if (*tv == NULL || (**tv & 0377) != CANONNET)
1985 	{
1986 		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
1987 badaddr:
1988 		/*
1989 		**  ExitStat may have been set by an earlier map open
1990 		**  failure (to a permanent error (EX_OSERR) in syserr())
1991 		**  so we also need to check if this particular $#error
1992 		**  return wanted a 4XX failure.
1993 		**
1994 		**  XXX the real fix is probably to set ExitStat correctly,
1995 		**  i.e., to EX_TEMPFAIL if the map open is just a temporary
1996 		**  error.
1997 		*/
1998 
1999 		if (ExitStat == EX_TEMPFAIL || tempfail)
2000 			a->q_state = QS_QUEUEUP;
2001 		else
2002 		{
2003 			a->q_state = QS_BADADDR;
2004 			a->q_mailer = &errormailer;
2005 		}
2006 		return a;
2007 	}
2008 	mname = *++tv;
2009 	--maxatom;
2010 
2011 	/* extract host and user portions */
2012 	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
2013 	{
2014 		hostp = ++tv;
2015 		--maxatom;
2016 	}
2017 	else
2018 		hostp = NULL;
2019 	--maxatom;
2020 	while (*tv != NULL && (**tv & 0377) != CANONUSER)
2021 	{
2022 		tv++;
2023 		--maxatom;
2024 	}
2025 	if (*tv == NULL)
2026 	{
2027 		syserr("554 5.3.5 buildaddr: no user");
2028 		goto badaddr;
2029 	}
2030 	if (tv == hostp)
2031 		hostp = NULL;
2032 	else if (hostp != NULL)
2033 		cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false);
2034 	cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false);
2035 	--maxatom;
2036 
2037 	/* save away the host name */
2038 	if (sm_strcasecmp(mname, "error") == 0)
2039 	{
2040 		/* Set up triplet for use by -bv */
2041 		a->q_mailer = &errormailer;
2042 		a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
2043 		/* XXX wrong place? */
2044 
2045 		if (hostp != NULL)
2046 		{
2047 			register struct errcodes *ep;
2048 
2049 			a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
2050 			if (strchr(hbuf, '.') != NULL)
2051 			{
2052 				a->q_status = sm_rpool_strdup_x(e->e_rpool,
2053 								hbuf);
2054 				setstat(dsntoexitstat(hbuf));
2055 			}
2056 			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
2057 			{
2058 				setstat(atoi(hbuf));
2059 			}
2060 			else
2061 			{
2062 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
2063 					if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
2064 						break;
2065 				setstat(ep->ec_code);
2066 			}
2067 		}
2068 		else
2069 		{
2070 			a->q_host = NULL;
2071 			setstat(EX_UNAVAILABLE);
2072 		}
2073 		stripquotes(ubuf);
2074 		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
2075 		{
2076 			char fmt[16];
2077 			int off;
2078 
2079 			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
2080 			{
2081 				ubuf[off + 4] = '\0';
2082 				off += 5;
2083 			}
2084 			else
2085 			{
2086 				off = 4;
2087 				ubuf[3] = '\0';
2088 			}
2089 			(void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s");
2090 			if (off > 4)
2091 				usrerr(fmt, ubuf + off);
2092 			else if (isenhsc(hbuf, '\0') > 0)
2093 				usrerrenh(hbuf, fmt, ubuf + off);
2094 			else
2095 				usrerr(fmt, ubuf + off);
2096 			/* XXX ubuf[off - 1] = ' '; */
2097 			if (ubuf[0] == '4')
2098 				tempfail = true;
2099 		}
2100 		else
2101 		{
2102 			usrerr("553 5.3.0 %s", ubuf);
2103 		}
2104 		goto badaddr;
2105 	}
2106 
2107 	for (mp = Mailer; (m = *mp++) != NULL; )
2108 	{
2109 		if (sm_strcasecmp(m->m_name, mname) == 0)
2110 			break;
2111 	}
2112 	if (m == NULL)
2113 	{
2114 		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
2115 		goto badaddr;
2116 	}
2117 	a->q_mailer = m;
2118 
2119 	/* figure out what host (if any) */
2120 	if (hostp == NULL)
2121 	{
2122 		if (!bitnset(M_LOCALMAILER, m->m_flags))
2123 		{
2124 			syserr("554 5.3.5 buildaddr: no host");
2125 			goto badaddr;
2126 		}
2127 		a->q_host = NULL;
2128 	}
2129 	else
2130 		a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
2131 
2132 	/* figure out the user */
2133 	p = ubuf;
2134 	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
2135 	{
2136 		p++;
2137 		tv++;
2138 		--maxatom;
2139 		a->q_flags |= QNOTREMOTE;
2140 	}
2141 
2142 	/* do special mapping for local mailer */
2143 	if (*p == '"')
2144 		p++;
2145 	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
2146 		a->q_mailer = m = ProgMailer;
2147 	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
2148 		a->q_mailer = m = FileMailer;
2149 	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
2150 	{
2151 		/* may be :include: */
2152 		stripquotes(ubuf);
2153 		if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
2154 		{
2155 			/* if :include:, don't need further rewriting */
2156 			a->q_mailer = m = InclMailer;
2157 			a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
2158 			return a;
2159 		}
2160 	}
2161 
2162 	/* rewrite according recipient mailer rewriting rules */
2163 	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2164 
2165 	if (ConfigLevel >= 10 ||
2166 	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
2167 	{
2168 		/* sender addresses done later */
2169 		(void) rewrite(tv, 2, 0, e, maxatom);
2170 		if (m->m_re_rwset > 0)
2171 		       (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom);
2172 	}
2173 	(void) rewrite(tv, 4, 0, e, maxatom);
2174 
2175 	/* save the result for the command line/RCPT argument */
2176 	cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true);
2177 	a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
2178 
2179 	/*
2180 	**  Do mapping to lower case as requested by mailer
2181 	*/
2182 
2183 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
2184 		makelower(a->q_host);
2185 	if (!bitnset(M_USR_UPPER, m->m_flags))
2186 		makelower(a->q_user);
2187 
2188 	if (tTd(24, 6))
2189 	{
2190 		sm_dprintf("buildaddr => ");
2191 		printaddr(sm_debug_file(), a, false);
2192 	}
2193 	return a;
2194 }
2195 
2196 /*
2197 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
2198 **
2199 **	Parameters:
2200 **		pvp -- parameter vector to rebuild.
2201 **		evp -- last parameter to include.  Can be NULL to
2202 **			use entire pvp.
2203 **		buf -- buffer to build the string into.
2204 **		sz -- size of buf.
2205 **		spacesub -- the space separator character; if '\0',
2206 **			use SpaceSub.
2207 **		external -- convert to external form?
2208 **			(no metacharacters; METAQUOTEs removed, see below)
2209 **
2210 **	Returns:
2211 **		none.
2212 **
2213 **	Side Effects:
2214 **		Destroys buf.
2215 **
2216 **	Notes:
2217 **	There are two formats for strings: internal and external.
2218 **	The external format is just an eight-bit clean string (no
2219 **	null bytes, everything else OK).  The internal format can
2220 **	include sendmail metacharacters.  The special character
2221 **	METAQUOTE essentially quotes the character following, stripping
2222 **	it of all special semantics.
2223 **
2224 **	The cataddr routine needs to be aware of whether it is producing
2225 **	an internal or external form as output (it only takes internal
2226 **	form as input).
2227 **
2228 **	The parseaddr routine has a similar issue on input, but that
2229 **	is flagged on the basis of which token table is passed in.
2230 */
2231 
2232 void
2233 cataddr(pvp, evp, buf, sz, spacesub, external)
2234 	char **pvp;
2235 	char **evp;
2236 	char *buf;
2237 	register int sz;
2238 	int spacesub;
2239 	bool external;
2240 {
2241 	bool oatomtok, natomtok;
2242 	char *p;
2243 
2244 	oatomtok = natomtok = false;
2245 	if (tTd(59, 14))
2246 	{
2247 		sm_dprintf("cataddr(%d) <==", external);
2248 		printav(sm_debug_file(), pvp);
2249 	}
2250 
2251 	if (sz <= 0)
2252 		return;
2253 
2254 	if (spacesub == '\0')
2255 		spacesub = SpaceSub;
2256 
2257 	if (pvp == NULL)
2258 	{
2259 		*buf = '\0';
2260 		return;
2261 	}
2262 	p = buf;
2263 	sz -= 2;
2264 	while (*pvp != NULL && sz > 0)
2265 	{
2266 		char *q;
2267 
2268 		natomtok = (ExtTokenTab[**pvp & 0xff] == ATM);
2269 		if (oatomtok && natomtok)
2270 		{
2271 			*p++ = spacesub;
2272 			if (--sz <= 0)
2273 				break;
2274 		}
2275 		for (q = *pvp; *q != '\0'; )
2276 		{
2277 			int c;
2278 
2279 			if (--sz <= 0)
2280 				break;
2281 			*p++ = c = *q++;
2282 
2283 			/*
2284 			**  If the current character (c) is METAQUOTE and we
2285 			**  want the "external" form and the next character
2286 			**  is not NUL, then overwrite METAQUOTE with that
2287 			**  character (i.e., METAQUOTE ch is changed to
2288 			**  ch).  p[-1] is used because p is advanced (above).
2289 			*/
2290 
2291 			if ((c & 0377) == METAQUOTE && external && *q != '\0')
2292 				p[-1] = *q++;
2293 		}
2294 		if (sz <= 0)
2295 			break;
2296 		oatomtok = natomtok;
2297 		if (pvp++ == evp)
2298 			break;
2299 	}
2300 
2301 #if 0
2302 	/*
2303 	**  Silently truncate long strings: even though this doesn't
2304 	**  seem like a good idea it is necessary because header checks
2305 	**  send the whole header value to rscheck() and hence rewrite().
2306 	**  The latter however sometimes uses a "short" buffer (e.g.,
2307 	**  cbuf[MAXNAME + 1]) to call cataddr() which then triggers this
2308 	**  error function.  One possible fix to the problem is to pass
2309 	**  flags to rscheck() and rewrite() to distinguish the various
2310 	**  calls and only trigger the error if necessary.  For now just
2311 	**  undo the change from 8.13.0.
2312 	*/
2313 
2314 	if (sz <= 0)
2315 		usrerr("cataddr: string too long");
2316 #endif
2317 	*p = '\0';
2318 
2319 	if (tTd(59, 14))
2320 		sm_dprintf("  cataddr => %s\n", str2prt(buf));
2321 }
2322 
2323 /*
2324 **  SAMEADDR -- Determine if two addresses are the same
2325 **
2326 **	This is not just a straight comparison -- if the mailer doesn't
2327 **	care about the host we just ignore it, etc.
2328 **
2329 **	Parameters:
2330 **		a, b -- pointers to the internal forms to compare.
2331 **
2332 **	Returns:
2333 **		true -- they represent the same mailbox.
2334 **		false -- they don't.
2335 **
2336 **	Side Effects:
2337 **		none.
2338 */
2339 
2340 bool
2341 sameaddr(a, b)
2342 	register ADDRESS *a;
2343 	register ADDRESS *b;
2344 {
2345 	register ADDRESS *ca, *cb;
2346 
2347 	/* if they don't have the same mailer, forget it */
2348 	if (a->q_mailer != b->q_mailer)
2349 		return false;
2350 
2351 	/* if the user isn't the same, we can drop out */
2352 	if (strcmp(a->q_user, b->q_user) != 0)
2353 		return false;
2354 
2355 	/* if we have good uids for both but they differ, these are different */
2356 	if (a->q_mailer == ProgMailer)
2357 	{
2358 		ca = getctladdr(a);
2359 		cb = getctladdr(b);
2360 		if (ca != NULL && cb != NULL &&
2361 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
2362 		    ca->q_uid != cb->q_uid)
2363 			return false;
2364 	}
2365 
2366 	/* otherwise compare hosts (but be careful for NULL ptrs) */
2367 	if (a->q_host == b->q_host)
2368 	{
2369 		/* probably both null pointers */
2370 		return true;
2371 	}
2372 	if (a->q_host == NULL || b->q_host == NULL)
2373 	{
2374 		/* only one is a null pointer */
2375 		return false;
2376 	}
2377 	if (strcmp(a->q_host, b->q_host) != 0)
2378 		return false;
2379 
2380 	return true;
2381 }
2382 /*
2383 **  PRINTADDR -- print address (for debugging)
2384 **
2385 **	Parameters:
2386 **		a -- the address to print
2387 **		follow -- follow the q_next chain.
2388 **
2389 **	Returns:
2390 **		none.
2391 **
2392 **	Side Effects:
2393 **		none.
2394 */
2395 
2396 struct qflags
2397 {
2398 	char		*qf_name;
2399 	unsigned long	qf_bit;
2400 };
2401 
2402 static struct qflags	AddressFlags[] =
2403 {
2404 	{ "QGOODUID",		QGOODUID	},
2405 	{ "QPRIMARY",		QPRIMARY	},
2406 	{ "QNOTREMOTE",		QNOTREMOTE	},
2407 	{ "QSELFREF",		QSELFREF	},
2408 	{ "QBOGUSSHELL",	QBOGUSSHELL	},
2409 	{ "QUNSAFEADDR",	QUNSAFEADDR	},
2410 	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
2411 	{ "QPINGONFAILURE",	QPINGONFAILURE	},
2412 	{ "QPINGONDELAY",	QPINGONDELAY	},
2413 	{ "QHASNOTIFY",		QHASNOTIFY	},
2414 	{ "QRELAYED",		QRELAYED	},
2415 	{ "QEXPANDED",		QEXPANDED	},
2416 	{ "QDELIVERED",		QDELIVERED	},
2417 	{ "QDELAYED",		QDELAYED	},
2418 	{ "QTHISPASS",		QTHISPASS	},
2419 	{ "QRCPTOK",		QRCPTOK		},
2420 	{ NULL,			0		}
2421 };
2422 
2423 void
2424 printaddr(fp, a, follow)
2425 	SM_FILE_T *fp;
2426 	register ADDRESS *a;
2427 	bool follow;
2428 {
2429 	register MAILER *m;
2430 	MAILER pseudomailer;
2431 	register struct qflags *qfp;
2432 	bool firstone;
2433 
2434 	if (a == NULL)
2435 	{
2436 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n");
2437 		return;
2438 	}
2439 
2440 	while (a != NULL)
2441 	{
2442 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a);
2443 		(void) sm_io_flush(fp, SM_TIME_DEFAULT);
2444 
2445 		/* find the mailer -- carefully */
2446 		m = a->q_mailer;
2447 		if (m == NULL)
2448 		{
2449 			m = &pseudomailer;
2450 			m->m_mno = -1;
2451 			m->m_name = "NULL";
2452 		}
2453 
2454 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2455 				     "%s:\n\tmailer %d (%s), host `%s'\n",
2456 				     a->q_paddr == NULL ? "<null>" : a->q_paddr,
2457 				     m->m_mno, m->m_name,
2458 				     a->q_host == NULL ? "<null>" : a->q_host);
2459 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2460 				     "\tuser `%s', ruser `%s'\n",
2461 				     a->q_user,
2462 				     a->q_ruser == NULL ? "<null>" : a->q_ruser);
2463 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate=");
2464 		switch (a->q_state)
2465 		{
2466 		  case QS_OK:
2467 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK");
2468 			break;
2469 
2470 		  case QS_DONTSEND:
2471 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2472 					     "DONTSEND");
2473 			break;
2474 
2475 		  case QS_BADADDR:
2476 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2477 					     "BADADDR");
2478 			break;
2479 
2480 		  case QS_QUEUEUP:
2481 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2482 					     "QUEUEUP");
2483 			break;
2484 
2485 		  case QS_RETRY:
2486 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY");
2487 			break;
2488 
2489 		  case QS_SENT:
2490 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT");
2491 			break;
2492 
2493 		  case QS_VERIFIED:
2494 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2495 					     "VERIFIED");
2496 			break;
2497 
2498 		  case QS_EXPANDED:
2499 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2500 					     "EXPANDED");
2501 			break;
2502 
2503 		  case QS_SENDER:
2504 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2505 					     "SENDER");
2506 			break;
2507 
2508 		  case QS_CLONED:
2509 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2510 					     "CLONED");
2511 			break;
2512 
2513 		  case QS_DISCARDED:
2514 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2515 					     "DISCARDED");
2516 			break;
2517 
2518 		  case QS_REPLACED:
2519 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2520 					     "REPLACED");
2521 			break;
2522 
2523 		  case QS_REMOVED:
2524 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2525 					     "REMOVED");
2526 			break;
2527 
2528 		  case QS_DUPLICATE:
2529 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2530 					     "DUPLICATE");
2531 			break;
2532 
2533 		  case QS_INCLUDED:
2534 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2535 					     "INCLUDED");
2536 			break;
2537 
2538 		  default:
2539 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2540 					     "%d", a->q_state);
2541 			break;
2542 		}
2543 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2544 				     ", next=%p, alias %p, uid %d, gid %d\n",
2545 				     a->q_next, a->q_alias,
2546 				     (int) a->q_uid, (int) a->q_gid);
2547 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<",
2548 				     a->q_flags);
2549 		firstone = true;
2550 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
2551 		{
2552 			if (!bitset(qfp->qf_bit, a->q_flags))
2553 				continue;
2554 			if (!firstone)
2555 				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2556 						     ",");
2557 			firstone = false;
2558 			(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
2559 					     qfp->qf_name);
2560 		}
2561 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n");
2562 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2563 				     "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
2564 				     a->q_owner == NULL ? "(none)" : a->q_owner,
2565 				     a->q_home == NULL ? "(none)" : a->q_home,
2566 				     a->q_fullname == NULL ? "(none)" : a->q_fullname);
2567 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2568 				     "\torcpt=\"%s\", statmta=%s, status=%s\n",
2569 				     a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
2570 				     a->q_statmta == NULL ? "(none)" : a->q_statmta,
2571 				     a->q_status == NULL ? "(none)" : a->q_status);
2572 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2573 				     "\tfinalrcpt=\"%s\"\n",
2574 				     a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
2575 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2576 				     "\trstatus=\"%s\"\n",
2577 				     a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2578 		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2579 				     "\tstatdate=%s\n",
2580 				     a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
2581 
2582 		if (!follow)
2583 			return;
2584 		a = a->q_next;
2585 	}
2586 }
2587 /*
2588 **  EMPTYADDR -- return true if this address is empty (``<>'')
2589 **
2590 **	Parameters:
2591 **		a -- pointer to the address
2592 **
2593 **	Returns:
2594 **		true -- if this address is "empty" (i.e., no one should
2595 **			ever generate replies to it.
2596 **		false -- if it is a "regular" (read: replyable) address.
2597 */
2598 
2599 bool
2600 emptyaddr(a)
2601 	register ADDRESS *a;
2602 {
2603 	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2604 	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2605 }
2606 /*
2607 **  REMOTENAME -- return the name relative to the current mailer
2608 **
2609 **	Parameters:
2610 **		name -- the name to translate.
2611 **		m -- the mailer that we want to do rewriting relative to.
2612 **		flags -- fine tune operations.
2613 **		pstat -- pointer to status word.
2614 **		e -- the current envelope.
2615 **
2616 **	Returns:
2617 **		the text string representing this address relative to
2618 **			the receiving mailer.
2619 **
2620 **	Side Effects:
2621 **		none.
2622 **
2623 **	Warnings:
2624 **		The text string returned is tucked away locally;
2625 **			copy it if you intend to save it.
2626 */
2627 
2628 char *
2629 remotename(name, m, flags, pstat, e)
2630 	char *name;
2631 	struct mailer *m;
2632 	int flags;
2633 	int *pstat;
2634 	register ENVELOPE *e;
2635 {
2636 	register char **pvp;
2637 	char *SM_NONVOLATILE fancy;
2638 	char *oldg;
2639 	int rwset;
2640 	static char buf[MAXNAME + 1];
2641 	char lbuf[MAXNAME + 1];
2642 	char pvpbuf[PSBUFSIZE];
2643 	char addrtype[4];
2644 
2645 	if (tTd(12, 1))
2646 	{
2647 		sm_dprintf("remotename(");
2648 		xputs(sm_debug_file(), name);
2649 		sm_dprintf(")\n");
2650 	}
2651 
2652 	/* don't do anything if we are tagging it as special */
2653 	if (bitset(RF_SENDERADDR, flags))
2654 	{
2655 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2656 						     : m->m_se_rwset;
2657 		addrtype[2] = 's';
2658 	}
2659 	else
2660 	{
2661 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2662 						     : m->m_re_rwset;
2663 		addrtype[2] = 'r';
2664 	}
2665 	if (rwset < 0)
2666 		return name;
2667 	addrtype[1] = ' ';
2668 	addrtype[3] = '\0';
2669 	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
2670 	macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
2671 
2672 	/*
2673 	**  Do a heuristic crack of this name to extract any comment info.
2674 	**	This will leave the name as a comment and a $g macro.
2675 	*/
2676 
2677 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2678 		fancy = "\201g";
2679 	else
2680 		fancy = crackaddr(name, e);
2681 
2682 	/*
2683 	**  Turn the name into canonical form.
2684 	**	Normally this will be RFC 822 style, i.e., "user@domain".
2685 	**	If this only resolves to "user", and the "C" flag is
2686 	**	specified in the sending mailer, then the sender's
2687 	**	domain will be appended.
2688 	*/
2689 
2690 	pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false);
2691 	if (pvp == NULL)
2692 		return name;
2693 	if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2694 		*pstat = EX_TEMPFAIL;
2695 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2696 	{
2697 		/* append from domain to this address */
2698 		register char **pxp = pvp;
2699 		int l = MAXATOM;	/* size of buffer for pvp */
2700 
2701 		/* see if there is an "@domain" in the current name */
2702 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2703 		{
2704 			pxp++;
2705 			--l;
2706 		}
2707 		if (*pxp == NULL)
2708 		{
2709 			/* no.... append the "@domain" from the sender */
2710 			register char **qxq = e->e_fromdomain;
2711 
2712 			while ((*pxp++ = *qxq++) != NULL)
2713 			{
2714 				if (--l <= 0)
2715 				{
2716 					*--pxp = NULL;
2717 					usrerr("553 5.1.0 remotename: too many tokens");
2718 					*pstat = EX_UNAVAILABLE;
2719 					break;
2720 				}
2721 			}
2722 			if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2723 				*pstat = EX_TEMPFAIL;
2724 		}
2725 	}
2726 
2727 	/*
2728 	**  Do more specific rewriting.
2729 	**	Rewrite using ruleset 1 or 2 depending on whether this is
2730 	**		a sender address or not.
2731 	**	Then run it through any receiving-mailer-specific rulesets.
2732 	*/
2733 
2734 	if (bitset(RF_SENDERADDR, flags))
2735 	{
2736 		if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
2737 			*pstat = EX_TEMPFAIL;
2738 	}
2739 	else
2740 	{
2741 		if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
2742 			*pstat = EX_TEMPFAIL;
2743 	}
2744 	if (rwset > 0)
2745 	{
2746 		if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
2747 			*pstat = EX_TEMPFAIL;
2748 	}
2749 
2750 	/*
2751 	**  Do any final sanitation the address may require.
2752 	**	This will normally be used to turn internal forms
2753 	**	(e.g., user@host.LOCAL) into external form.  This
2754 	**	may be used as a default to the above rules.
2755 	*/
2756 
2757 	if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
2758 		*pstat = EX_TEMPFAIL;
2759 
2760 	/*
2761 	**  Now restore the comment information we had at the beginning.
2762 	*/
2763 
2764 	cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false);
2765 	oldg = macget(&e->e_macro, 'g');
2766 	macset(&e->e_macro, 'g', lbuf);
2767 
2768 	SM_TRY
2769 		/* need to make sure route-addrs have <angle brackets> */
2770 		if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2771 			expand("<\201g>", buf, sizeof(buf), e);
2772 		else
2773 			expand(fancy, buf, sizeof(buf), e);
2774 	SM_FINALLY
2775 		macset(&e->e_macro, 'g', oldg);
2776 	SM_END_TRY
2777 
2778 	if (tTd(12, 1))
2779 	{
2780 		sm_dprintf("remotename => `");
2781 		xputs(sm_debug_file(), buf);
2782 		sm_dprintf("'\n");
2783 	}
2784 	return buf;
2785 }
2786 /*
2787 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2788 **
2789 **	Parameters:
2790 **		a -- the address to map (but just the user name part).
2791 **		sendq -- the sendq in which to install any replacement
2792 **			addresses.
2793 **		aliaslevel -- the alias nesting depth.
2794 **		e -- the envelope.
2795 **
2796 **	Returns:
2797 **		none.
2798 */
2799 
2800 #define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2801 			 Q_PINGFLAGS|QHASNOTIFY|\
2802 			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
2803 			 QBYTRACE|QBYNDELAY|QBYNRELAY)
2804 
2805 void
2806 maplocaluser(a, sendq, aliaslevel, e)
2807 	register ADDRESS *a;
2808 	ADDRESS **sendq;
2809 	int aliaslevel;
2810 	ENVELOPE *e;
2811 {
2812 	register char **pvp;
2813 	register ADDRESS *SM_NONVOLATILE a1 = NULL;
2814 	char pvpbuf[PSBUFSIZE];
2815 
2816 	if (tTd(29, 1))
2817 	{
2818 		sm_dprintf("maplocaluser: ");
2819 		printaddr(sm_debug_file(), a, false);
2820 	}
2821 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL,
2822 			false);
2823 	if (pvp == NULL)
2824 	{
2825 		if (tTd(29, 9))
2826 			sm_dprintf("maplocaluser: cannot prescan %s\n",
2827 				a->q_user);
2828 		return;
2829 	}
2830 
2831 	macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2832 	macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
2833 	macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
2834 
2835 	macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
2836 	if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
2837 	{
2838 		if (tTd(29, 9))
2839 			sm_dprintf("maplocaluser: rewrite tempfail\n");
2840 		a->q_state = QS_QUEUEUP;
2841 		a->q_status = "4.4.3";
2842 		return;
2843 	}
2844 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2845 	{
2846 		if (tTd(29, 9))
2847 			sm_dprintf("maplocaluser: doesn't resolve\n");
2848 		return;
2849 	}
2850 
2851 	SM_TRY
2852 		a1 = buildaddr(pvp, NULL, 0, e);
2853 	SM_EXCEPT(exc, "E:mta.quickabort")
2854 
2855 		/*
2856 		**  mark address as bad, S5 returned an error
2857 		**	and we gave that back to the SMTP client.
2858 		*/
2859 
2860 		a->q_state = QS_DONTSEND;
2861 		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
2862 	SM_END_TRY
2863 
2864 	/* if non-null, mailer destination specified -- has it changed? */
2865 	if (a1 == NULL || sameaddr(a, a1))
2866 	{
2867 		if (tTd(29, 9))
2868 			sm_dprintf("maplocaluser: address unchanged\n");
2869 		return;
2870 	}
2871 
2872 	/* make new address take on flags and print attributes of old */
2873 	a1->q_flags &= ~Q_COPYFLAGS;
2874 	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2875 	a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
2876 	a1->q_finalrcpt = a->q_finalrcpt;
2877 	a1->q_orcpt = a->q_orcpt;
2878 
2879 	/* mark old address as dead; insert new address */
2880 	a->q_state = QS_REPLACED;
2881 	if (tTd(29, 5))
2882 	{
2883 		sm_dprintf("maplocaluser: QS_REPLACED ");
2884 		printaddr(sm_debug_file(), a, false);
2885 	}
2886 	a1->q_alias = a;
2887 	allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
2888 	(void) recipient(a1, sendq, aliaslevel, e);
2889 }
2890 /*
2891 **  DEQUOTE_INIT -- initialize dequote map
2892 **
2893 **	Parameters:
2894 **		map -- the internal map structure.
2895 **		args -- arguments.
2896 **
2897 **	Returns:
2898 **		true.
2899 */
2900 
2901 bool
2902 dequote_init(map, args)
2903 	MAP *map;
2904 	char *args;
2905 {
2906 	register char *p = args;
2907 
2908 	/* there is no check whether there is really an argument */
2909 	map->map_mflags |= MF_KEEPQUOTES;
2910 	for (;;)
2911 	{
2912 		while (isascii(*p) && isspace(*p))
2913 			p++;
2914 		if (*p != '-')
2915 			break;
2916 		switch (*++p)
2917 		{
2918 		  case 'a':
2919 			map->map_app = ++p;
2920 			break;
2921 
2922 		  case 'D':
2923 			map->map_mflags |= MF_DEFER;
2924 			break;
2925 
2926 		  case 'S':
2927 		  case 's':
2928 			map->map_spacesub = *++p;
2929 			break;
2930 		}
2931 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2932 			p++;
2933 		if (*p != '\0')
2934 			*p = '\0';
2935 	}
2936 	if (map->map_app != NULL)
2937 		map->map_app = newstr(map->map_app);
2938 
2939 	return true;
2940 }
2941 /*
2942 **  DEQUOTE_MAP -- unquote an address
2943 **
2944 **	Parameters:
2945 **		map -- the internal map structure (ignored).
2946 **		name -- the name to dequote.
2947 **		av -- arguments (ignored).
2948 **		statp -- pointer to status out-parameter.
2949 **
2950 **	Returns:
2951 **		NULL -- if there were no quotes, or if the resulting
2952 **			unquoted buffer would not be acceptable to prescan.
2953 **		else -- The dequoted buffer.
2954 */
2955 
2956 /* ARGSUSED2 */
2957 char *
2958 dequote_map(map, name, av, statp)
2959 	MAP *map;
2960 	char *name;
2961 	char **av;
2962 	int *statp;
2963 {
2964 	register char *p;
2965 	register char *q;
2966 	register char c;
2967 	int anglecnt = 0;
2968 	int cmntcnt = 0;
2969 	int quotecnt = 0;
2970 	int spacecnt = 0;
2971 	bool quotemode = false;
2972 	bool bslashmode = false;
2973 	char spacesub = map->map_spacesub;
2974 
2975 	for (p = q = name; (c = *p++) != '\0'; )
2976 	{
2977 		if (bslashmode)
2978 		{
2979 			bslashmode = false;
2980 			*q++ = c;
2981 			continue;
2982 		}
2983 
2984 		if (c == ' ' && spacesub != '\0')
2985 			c = spacesub;
2986 
2987 		switch (c)
2988 		{
2989 		  case '\\':
2990 			bslashmode = true;
2991 			break;
2992 
2993 		  case '(':
2994 			cmntcnt++;
2995 			break;
2996 
2997 		  case ')':
2998 			if (cmntcnt-- <= 0)
2999 				return NULL;
3000 			break;
3001 
3002 		  case ' ':
3003 		  case '\t':
3004 			spacecnt++;
3005 			break;
3006 		}
3007 
3008 		if (cmntcnt > 0)
3009 		{
3010 			*q++ = c;
3011 			continue;
3012 		}
3013 
3014 		switch (c)
3015 		{
3016 		  case '"':
3017 			quotemode = !quotemode;
3018 			quotecnt++;
3019 			continue;
3020 
3021 		  case '<':
3022 			anglecnt++;
3023 			break;
3024 
3025 		  case '>':
3026 			if (anglecnt-- <= 0)
3027 				return NULL;
3028 			break;
3029 		}
3030 		*q++ = c;
3031 	}
3032 
3033 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
3034 	    quotemode || quotecnt <= 0 || spacecnt != 0)
3035 		return NULL;
3036 	*q++ = '\0';
3037 	return map_rewrite(map, name, strlen(name), NULL);
3038 }
3039 /*
3040 **  RSCHECK -- check string(s) for validity using rewriting sets
3041 **
3042 **	Parameters:
3043 **		rwset -- the rewriting set to use.
3044 **		p1 -- the first string to check.
3045 **		p2 -- the second string to check -- may be null.
3046 **		e -- the current envelope.
3047 **		flags -- control some behavior, see RSF_ in sendmail.h
3048 **		logl -- logging level.
3049 **		host -- NULL or relay host.
3050 **		logid -- id for sm_syslog.
3051 **		addr -- if not NULL and ruleset returns $#error:
3052 **				store mailer triple here.
3053 **
3054 **	Returns:
3055 **		EX_OK -- if the rwset doesn't resolve to $#error
3056 **		else -- the failure status (message printed)
3057 */
3058 
3059 int
3060 rscheck(rwset, p1, p2, e, flags, logl, host, logid, addr)
3061 	char *rwset;
3062 	char *p1;
3063 	char *p2;
3064 	ENVELOPE *e;
3065 	int flags;
3066 	int logl;
3067 	char *host;
3068 	char *logid;
3069 	ADDRESS *addr;
3070 {
3071 	char *volatile buf;
3072 	size_t bufsize;
3073 	int saveexitstat;
3074 	int volatile rstat = EX_OK;
3075 	char **pvp;
3076 	int rsno;
3077 	bool volatile discard = false;
3078 	bool saveQuickAbort = QuickAbort;
3079 	bool saveSuprErrs = SuprErrs;
3080 	bool quarantine = false;
3081 	char ubuf[BUFSIZ * 2];
3082 	char buf0[MAXLINE];
3083 	char pvpbuf[PSBUFSIZE];
3084 	extern char MsgBuf[];
3085 
3086 	if (tTd(48, 2))
3087 		sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
3088 			p2 == NULL ? "(NULL)" : p2);
3089 
3090 	rsno = strtorwset(rwset, NULL, ST_FIND);
3091 	if (rsno < 0)
3092 		return EX_OK;
3093 
3094 	if (p2 != NULL)
3095 	{
3096 		bufsize = strlen(p1) + strlen(p2) + 2;
3097 		if (bufsize > sizeof(buf0))
3098 			buf = sm_malloc_x(bufsize);
3099 		else
3100 		{
3101 			buf = buf0;
3102 			bufsize = sizeof(buf0);
3103 		}
3104 		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
3105 	}
3106 	else
3107 	{
3108 		bufsize = strlen(p1) + 1;
3109 		if (bufsize > sizeof(buf0))
3110 			buf = sm_malloc_x(bufsize);
3111 		else
3112 		{
3113 			buf = buf0;
3114 			bufsize = sizeof(buf0);
3115 		}
3116 		(void) sm_strlcpy(buf, p1, bufsize);
3117 	}
3118 	SM_TRY
3119 	{
3120 		SuprErrs = true;
3121 		QuickAbort = false;
3122 		pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL,
3123 			      bitset(RSF_RMCOMM, flags) ?
3124 					IntTokenTab : TokTypeNoC,
3125 			      bitset(RSF_RMCOMM, flags) ? false : true);
3126 		SuprErrs = saveSuprErrs;
3127 		if (pvp == NULL)
3128 		{
3129 			if (tTd(48, 2))
3130 				sm_dprintf("rscheck: cannot prescan input\n");
3131 	/*
3132 			syserr("rscheck: cannot prescan input: \"%s\"",
3133 				shortenstring(buf, MAXSHORTSTR));
3134 			rstat = EX_DATAERR;
3135 	*/
3136 			goto finis;
3137 		}
3138 		if (bitset(RSF_UNSTRUCTURED, flags))
3139 			SuprErrs = true;
3140 		(void) REWRITE(pvp, rsno, e);
3141 		if (bitset(RSF_UNSTRUCTURED, flags))
3142 			SuprErrs = saveSuprErrs;
3143 		if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
3144 		    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
3145 				       strcmp(pvp[1], "discard") != 0))
3146 		{
3147 			goto finis;
3148 		}
3149 
3150 		if (strcmp(pvp[1], "discard") == 0)
3151 		{
3152 			if (tTd(48, 2))
3153 				sm_dprintf("rscheck: discard mailer selected\n");
3154 			e->e_flags |= EF_DISCARD;
3155 			discard = true;
3156 		}
3157 		else if (strcmp(pvp[1], "error") == 0 &&
3158 			 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
3159 			 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
3160 		{
3161 			if (pvp[4] == NULL ||
3162 			    (pvp[4][0] & 0377) != CANONUSER ||
3163 			    pvp[5] == NULL)
3164 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3165 								 rwset);
3166 			else
3167 			{
3168 				cataddr(&(pvp[5]), NULL, ubuf,
3169 					sizeof(ubuf), ' ', true);
3170 				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3171 								 ubuf);
3172 			}
3173 			macdefine(&e->e_macro, A_PERM,
3174 				  macid("{quarantine}"), e->e_quarmsg);
3175 			quarantine = true;
3176 		}
3177 		else
3178 		{
3179 			auto ADDRESS a1;
3180 			int savelogusrerrs = LogUsrErrs;
3181 			static bool logged = false;
3182 
3183 			/* got an error -- process it */
3184 			saveexitstat = ExitStat;
3185 			LogUsrErrs = false;
3186 			(void) buildaddr(pvp, &a1, 0, e);
3187 			if (addr != NULL)
3188 			{
3189 				addr->q_mailer = a1.q_mailer;
3190 				addr->q_user = a1.q_user;
3191 				addr->q_host = a1.q_host;
3192 			}
3193 			LogUsrErrs = savelogusrerrs;
3194 			rstat = ExitStat;
3195 			ExitStat = saveexitstat;
3196 			if (!logged)
3197 			{
3198 				if (bitset(RSF_COUNT, flags))
3199 					markstats(e, &a1, STATS_REJECT);
3200 				logged = true;
3201 			}
3202 		}
3203 
3204 		if (LogLevel > logl)
3205 		{
3206 			char *relay;
3207 			char *p;
3208 			char lbuf[MAXLINE];
3209 
3210 			p = lbuf;
3211 			if (p2 != NULL)
3212 			{
3213 				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
3214 					", arg2=%s",
3215 					p2);
3216 				p += strlen(p);
3217 			}
3218 
3219 			if (host != NULL)
3220 				relay = host;
3221 			else
3222 				relay = macvalue('_', e);
3223 			if (relay != NULL)
3224 			{
3225 				(void) sm_snprintf(p, SPACELEFT(lbuf, p),
3226 					", relay=%s", relay);
3227 				p += strlen(p);
3228 			}
3229 			*p = '\0';
3230 			if (discard)
3231 				sm_syslog(LOG_NOTICE, logid,
3232 					  "ruleset=%s, arg1=%s%s, discard",
3233 					  rwset, p1, lbuf);
3234 			else if (quarantine)
3235 				sm_syslog(LOG_NOTICE, logid,
3236 					  "ruleset=%s, arg1=%s%s, quarantine=%s",
3237 					  rwset, p1, lbuf, ubuf);
3238 			else
3239 				sm_syslog(LOG_NOTICE, logid,
3240 					  "ruleset=%s, arg1=%s%s, reject=%s",
3241 					  rwset, p1, lbuf, MsgBuf);
3242 		}
3243 
3244 	 finis: ;
3245 	}
3246 	SM_FINALLY
3247 	{
3248 		/* clean up */
3249 		if (buf != buf0)
3250 			sm_free(buf);
3251 		QuickAbort = saveQuickAbort;
3252 	}
3253 	SM_END_TRY
3254 
3255 	setstat(rstat);
3256 
3257 	/* rulesets don't set errno */
3258 	errno = 0;
3259 	if (rstat != EX_OK && QuickAbort)
3260 		sm_exc_raisenew_x(&EtypeQuickAbort, 2);
3261 	return rstat;
3262 }
3263 /*
3264 **  RSCAP -- call rewriting set to return capabilities
3265 **
3266 **	Parameters:
3267 **		rwset -- the rewriting set to use.
3268 **		p1 -- the first string to check.
3269 **		p2 -- the second string to check -- may be null.
3270 **		e -- the current envelope.
3271 **		pvp -- pointer to token vector.
3272 **		pvpbuf -- buffer space.
3273 **		size -- size of buffer space.
3274 **
3275 **	Returns:
3276 **		EX_UNAVAILABLE -- ruleset doesn't exist.
3277 **		EX_DATAERR -- prescan() failed.
3278 **		EX_OK -- rewrite() was successful.
3279 **		else -- return status from rewrite().
3280 */
3281 
3282 int
3283 rscap(rwset, p1, p2, e, pvp, pvpbuf, size)
3284 	char *rwset;
3285 	char *p1;
3286 	char *p2;
3287 	ENVELOPE *e;
3288 	char ***pvp;
3289 	char *pvpbuf;
3290 	int size;
3291 {
3292 	char *volatile buf;
3293 	size_t bufsize;
3294 	int volatile rstat = EX_OK;
3295 	int rsno;
3296 	bool saveQuickAbort = QuickAbort;
3297 	bool saveSuprErrs = SuprErrs;
3298 	char buf0[MAXLINE];
3299 	extern char MsgBuf[];
3300 
3301 	if (tTd(48, 2))
3302 		sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
3303 			p2 == NULL ? "(NULL)" : p2);
3304 
3305 	SM_REQUIRE(pvp != NULL);
3306 	rsno = strtorwset(rwset, NULL, ST_FIND);
3307 	if (rsno < 0)
3308 		return EX_UNAVAILABLE;
3309 
3310 	if (p2 != NULL)
3311 	{
3312 		bufsize = strlen(p1) + strlen(p2) + 2;
3313 		if (bufsize > sizeof(buf0))
3314 			buf = sm_malloc_x(bufsize);
3315 		else
3316 		{
3317 			buf = buf0;
3318 			bufsize = sizeof(buf0);
3319 		}
3320 		(void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
3321 	}
3322 	else
3323 	{
3324 		bufsize = strlen(p1) + 1;
3325 		if (bufsize > sizeof(buf0))
3326 			buf = sm_malloc_x(bufsize);
3327 		else
3328 		{
3329 			buf = buf0;
3330 			bufsize = sizeof(buf0);
3331 		}
3332 		(void) sm_strlcpy(buf, p1, bufsize);
3333 	}
3334 	SM_TRY
3335 	{
3336 		SuprErrs = true;
3337 		QuickAbort = false;
3338 		*pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab,
3339 				false);
3340 		if (*pvp != NULL)
3341 			rstat = rewrite(*pvp, rsno, 0, e, size);
3342 		else
3343 		{
3344 			if (tTd(48, 2))
3345 				sm_dprintf("rscap: cannot prescan input\n");
3346 			rstat = EX_DATAERR;
3347 		}
3348 	}
3349 	SM_FINALLY
3350 	{
3351 		/* clean up */
3352 		if (buf != buf0)
3353 			sm_free(buf);
3354 		SuprErrs = saveSuprErrs;
3355 		QuickAbort = saveQuickAbort;
3356 
3357 		/* prevent information leak, this may contain rewrite error */
3358 		MsgBuf[0] = '\0';
3359 	}
3360 	SM_END_TRY
3361 	return rstat;
3362 }
3363