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