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