xref: /freebsd/contrib/sendmail/src/parseaddr.c (revision 13058a916175518dfbac6ce66b9b8e22ecf43155)
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.12 2001/05/03 17:24:11 gshapiro 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 #define MAXMATCH	9	/* max params per rewrite */
771 
772 
773 int
774 rewrite(pvp, ruleset, reclevel, e)
775 	char **pvp;
776 	int ruleset;
777 	int reclevel;
778 	register ENVELOPE *e;
779 {
780 	register char *ap;		/* address pointer */
781 	register char *rp;		/* rewrite pointer */
782 	register char *rulename;	/* ruleset name */
783 	register char *prefix;
784 	register char **avp;		/* address vector pointer */
785 	register char **rvp;		/* rewrite vector pointer */
786 	register struct match *mlp;	/* cur ptr into mlist */
787 	register struct rewrite *rwr;	/* pointer to current rewrite rule */
788 	int ruleno;			/* current rule number */
789 	int rstat = EX_OK;		/* return status */
790 	int loopcount;
791 	struct match mlist[MAXMATCH];	/* stores match on LHS */
792 	char *npvp[MAXATOM + 1];	/* temporary space for rebuild */
793 	char buf[MAXLINE];
794 	char name[6];
795 
796 	if (ruleset < 0 || ruleset >= MAXRWSETS)
797 	{
798 		syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
799 		return EX_CONFIG;
800 	}
801 	rulename = RuleSetNames[ruleset];
802 	if (rulename == NULL)
803 	{
804 		snprintf(name, sizeof name, "%d", ruleset);
805 		rulename = name;
806 	}
807 	if (OpMode == MD_TEST)
808 		prefix = "";
809 	else
810 		prefix = "rewrite: ruleset ";
811 	if (OpMode == MD_TEST)
812 	{
813 		printf("%s%-16.16s   input:", prefix, rulename);
814 		printav(pvp);
815 	}
816 	else if (tTd(21, 1))
817 	{
818 		dprintf("%s%-16.16s   input:", prefix, rulename);
819 		printav(pvp);
820 	}
821 	if (reclevel++ > MaxRuleRecursion)
822 	{
823 		syserr("rewrite: excessive recursion (max %d), ruleset %s",
824 			MaxRuleRecursion, rulename);
825 		return EX_CONFIG;
826 	}
827 	if (pvp == NULL)
828 		return EX_USAGE;
829 
830 	/*
831 	**  Run through the list of rewrite rules, applying
832 	**	any that match.
833 	*/
834 
835 	ruleno = 1;
836 	loopcount = 0;
837 	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
838 	{
839 		int status;
840 
841 		/* if already canonical, quit now */
842 		if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
843 			break;
844 
845 		if (tTd(21, 12))
846 		{
847 			if (tTd(21, 15))
848 				dprintf("-----trying rule (line %d):",
849 				       rwr->r_line);
850 			else
851 				dprintf("-----trying rule:");
852 			printav(rwr->r_lhs);
853 		}
854 
855 		/* try to match on this rule */
856 		mlp = mlist;
857 		rvp = rwr->r_lhs;
858 		avp = pvp;
859 		if (++loopcount > 100)
860 		{
861 			syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
862 				rulename, ruleno);
863 			if (tTd(21, 1))
864 			{
865 				dprintf("workspace: ");
866 				printav(pvp);
867 			}
868 			break;
869 		}
870 
871 		while ((ap = *avp) != NULL || *rvp != NULL)
872 		{
873 			rp = *rvp;
874 			if (tTd(21, 35))
875 			{
876 				dprintf("ADVANCE rp=");
877 				xputs(rp);
878 				dprintf(", ap=");
879 				xputs(ap);
880 				dprintf("\n");
881 			}
882 			if (rp == NULL)
883 			{
884 				/* end-of-pattern before end-of-address */
885 				goto backup;
886 			}
887 			if (ap == NULL && (*rp & 0377) != MATCHZANY &&
888 			    (*rp & 0377) != MATCHZERO)
889 			{
890 				/* end-of-input with patterns left */
891 				goto backup;
892 			}
893 
894 			switch (*rp & 0377)
895 			{
896 			  case MATCHCLASS:
897 				/* match any phrase in a class */
898 				mlp->match_pattern = rvp;
899 				mlp->match_first = avp;
900 	extendclass:
901 				ap = *avp;
902 				if (ap == NULL)
903 					goto backup;
904 				mlp->match_last = avp++;
905 				cataddr(mlp->match_first, mlp->match_last,
906 					buf, sizeof buf, '\0');
907 				if (!wordinclass(buf, rp[1]))
908 				{
909 					if (tTd(21, 36))
910 					{
911 						dprintf("EXTEND  rp=");
912 						xputs(rp);
913 						dprintf(", ap=");
914 						xputs(ap);
915 						dprintf("\n");
916 					}
917 					goto extendclass;
918 				}
919 				if (tTd(21, 36))
920 					dprintf("CLMATCH\n");
921 				mlp++;
922 				break;
923 
924 			  case MATCHNCLASS:
925 				/* match any token not in a class */
926 				if (wordinclass(ap, rp[1]))
927 					goto backup;
928 
929 				/* FALLTHROUGH */
930 
931 			  case MATCHONE:
932 			  case MATCHANY:
933 				/* match exactly one token */
934 				mlp->match_pattern = rvp;
935 				mlp->match_first = avp;
936 				mlp->match_last = avp++;
937 				mlp++;
938 				break;
939 
940 			  case MATCHZANY:
941 				/* match zero or more tokens */
942 				mlp->match_pattern = rvp;
943 				mlp->match_first = avp;
944 				mlp->match_last = avp - 1;
945 				mlp++;
946 				break;
947 
948 			  case MATCHZERO:
949 				/* match zero tokens */
950 				break;
951 
952 			  case MACRODEXPAND:
953 				/*
954 				**  Match against run-time macro.
955 				**  This algorithm is broken for the
956 				**  general case (no recursive macros,
957 				**  improper tokenization) but should
958 				**  work for the usual cases.
959 				*/
960 
961 				ap = macvalue(rp[1], e);
962 				mlp->match_first = avp;
963 				if (tTd(21, 2))
964 					dprintf("rewrite: LHS $&%s => \"%s\"\n",
965 						macname(rp[1]),
966 						ap == NULL ? "(NULL)" : ap);
967 
968 				if (ap == NULL)
969 					break;
970 				while (*ap != '\0')
971 				{
972 					if (*avp == NULL ||
973 					    strncasecmp(ap, *avp, strlen(*avp)) != 0)
974 					{
975 						/* no match */
976 						avp = mlp->match_first;
977 						goto backup;
978 					}
979 					ap += strlen(*avp++);
980 				}
981 
982 				/* match */
983 				break;
984 
985 			  default:
986 				/* must have exact match */
987 				if (sm_strcasecmp(rp, ap))
988 					goto backup;
989 				avp++;
990 				break;
991 			}
992 
993 			/* successful match on this token */
994 			rvp++;
995 			continue;
996 
997 	  backup:
998 			/* match failed -- back up */
999 			while (--mlp >= mlist)
1000 			{
1001 				rvp = mlp->match_pattern;
1002 				rp = *rvp;
1003 				avp = mlp->match_last + 1;
1004 				ap = *avp;
1005 
1006 				if (tTd(21, 36))
1007 				{
1008 					dprintf("BACKUP  rp=");
1009 					xputs(rp);
1010 					dprintf(", ap=");
1011 					xputs(ap);
1012 					dprintf("\n");
1013 				}
1014 
1015 				if (ap == NULL)
1016 				{
1017 					/* run off the end -- back up again */
1018 					continue;
1019 				}
1020 				if ((*rp & 0377) == MATCHANY ||
1021 				    (*rp & 0377) == MATCHZANY)
1022 				{
1023 					/* extend binding and continue */
1024 					mlp->match_last = avp++;
1025 					rvp++;
1026 					mlp++;
1027 					break;
1028 				}
1029 				if ((*rp & 0377) == MATCHCLASS)
1030 				{
1031 					/* extend binding and try again */
1032 					mlp->match_last = avp;
1033 					goto extendclass;
1034 				}
1035 			}
1036 
1037 			if (mlp < mlist)
1038 			{
1039 				/* total failure to match */
1040 				break;
1041 			}
1042 		}
1043 
1044 		/*
1045 		**  See if we successfully matched
1046 		*/
1047 
1048 		if (mlp < mlist || *rvp != NULL)
1049 		{
1050 			if (tTd(21, 10))
1051 				dprintf("----- rule fails\n");
1052 			rwr = rwr->r_next;
1053 			ruleno++;
1054 			loopcount = 0;
1055 			continue;
1056 		}
1057 
1058 		rvp = rwr->r_rhs;
1059 		if (tTd(21, 12))
1060 		{
1061 			dprintf("-----rule matches:");
1062 			printav(rvp);
1063 		}
1064 
1065 		rp = *rvp;
1066 		if (rp != NULL)
1067 		{
1068 			if ((*rp & 0377) == CANONUSER)
1069 			{
1070 				rvp++;
1071 				rwr = rwr->r_next;
1072 				ruleno++;
1073 				loopcount = 0;
1074 			}
1075 			else if ((*rp & 0377) == CANONHOST)
1076 			{
1077 				rvp++;
1078 				rwr = NULL;
1079 			}
1080 		}
1081 
1082 		/* substitute */
1083 		for (avp = npvp; *rvp != NULL; rvp++)
1084 		{
1085 			register struct match *m;
1086 			register char **pp;
1087 
1088 			rp = *rvp;
1089 			if ((*rp & 0377) == MATCHREPL)
1090 			{
1091 				/* substitute from LHS */
1092 				m = &mlist[rp[1] - '1'];
1093 				if (m < mlist || m >= mlp)
1094 				{
1095 					syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
1096 						rulename, rp[1]);
1097 					return EX_CONFIG;
1098 				}
1099 				if (tTd(21, 15))
1100 				{
1101 					dprintf("$%c:", rp[1]);
1102 					pp = m->match_first;
1103 					while (pp <= m->match_last)
1104 					{
1105 						dprintf(" %lx=\"",
1106 							(u_long) *pp);
1107 						(void) dflush();
1108 						dprintf("%s\"", *pp++);
1109 					}
1110 					dprintf("\n");
1111 				}
1112 				pp = m->match_first;
1113 				while (pp <= m->match_last)
1114 				{
1115 					if (avp >= &npvp[MAXATOM])
1116 					{
1117 						syserr("554 5.3.0 rewrite: expansion too long");
1118 						return EX_DATAERR;
1119 					}
1120 					*avp++ = *pp++;
1121 				}
1122 			}
1123 			else
1124 			{
1125 				/* some sort of replacement */
1126 				if (avp >= &npvp[MAXATOM])
1127 				{
1128 	toolong:
1129 					syserr("554 5.3.0 rewrite: expansion too long");
1130 					return EX_DATAERR;
1131 				}
1132 				if ((*rp & 0377) != MACRODEXPAND)
1133 				{
1134 					/* vanilla replacement */
1135 					*avp++ = rp;
1136 				}
1137 				else
1138 				{
1139 					/* $&x replacement */
1140 					char *mval = macvalue(rp[1], e);
1141 					char **xpvp;
1142 					int trsize = 0;
1143 					static size_t pvpb1_size = 0;
1144 					static char **pvpb1 = NULL;
1145 					char pvpbuf[PSBUFSIZE];
1146 
1147 					if (tTd(21, 2))
1148 						dprintf("rewrite: RHS $&%s => \"%s\"\n",
1149 							macname(rp[1]),
1150 							mval == NULL ? "(NULL)" : mval);
1151 					if (mval == NULL || *mval == '\0')
1152 						continue;
1153 
1154 					/* save the remainder of the input */
1155 					for (xpvp = pvp; *xpvp != NULL; xpvp++)
1156 						trsize += sizeof *xpvp;
1157 					if ((size_t) trsize > pvpb1_size)
1158 					{
1159 						if (pvpb1 != NULL)
1160 							sm_free(pvpb1);
1161 						pvpb1 = (char **)xalloc(trsize);
1162 						pvpb1_size = trsize;
1163 					}
1164 
1165 					memmove((char *) pvpb1,
1166 						(char *) pvp,
1167 						trsize);
1168 
1169 					/* scan the new replacement */
1170 					xpvp = prescan(mval, '\0', pvpbuf,
1171 						       sizeof pvpbuf, NULL,
1172 						       NULL);
1173 					if (xpvp == NULL)
1174 					{
1175 						/* prescan pre-printed error */
1176 						return EX_DATAERR;
1177 					}
1178 
1179 					/* insert it into the output stream */
1180 					while (*xpvp != NULL)
1181 					{
1182 						if (tTd(21, 19))
1183 							dprintf(" ... %s\n",
1184 								*xpvp);
1185 						*avp++ = newstr(*xpvp);
1186 						if (avp >= &npvp[MAXATOM])
1187 							goto toolong;
1188 						xpvp++;
1189 					}
1190 					if (tTd(21, 19))
1191 						dprintf(" ... DONE\n");
1192 
1193 					/* restore the old trailing input */
1194 					memmove((char *) pvp,
1195 						(char *) pvpb1,
1196 						trsize);
1197 				}
1198 			}
1199 		}
1200 		*avp++ = NULL;
1201 
1202 		/*
1203 		**  Check for any hostname/keyword lookups.
1204 		*/
1205 
1206 		for (rvp = npvp; *rvp != NULL; rvp++)
1207 		{
1208 			char **hbrvp;
1209 			char **xpvp;
1210 			int trsize;
1211 			char *replac;
1212 			int endtoken;
1213 			STAB *map;
1214 			char *mapname;
1215 			char **key_rvp;
1216 			char **arg_rvp;
1217 			char **default_rvp;
1218 			char cbuf[MAXNAME + 1];
1219 			char *pvpb1[MAXATOM + 1];
1220 			char *argvect[10];
1221 			char pvpbuf[PSBUFSIZE];
1222 			char *nullpvp[1];
1223 
1224 			if ((**rvp & 0377) != HOSTBEGIN &&
1225 			    (**rvp & 0377) != LOOKUPBEGIN)
1226 				continue;
1227 
1228 			/*
1229 			**  Got a hostname/keyword lookup.
1230 			**
1231 			**	This could be optimized fairly easily.
1232 			*/
1233 
1234 			hbrvp = rvp;
1235 			if ((**rvp & 0377) == HOSTBEGIN)
1236 			{
1237 				endtoken = HOSTEND;
1238 				mapname = "host";
1239 			}
1240 			else
1241 			{
1242 				endtoken = LOOKUPEND;
1243 				mapname = *++rvp;
1244 			}
1245 			map = stab(mapname, ST_MAP, ST_FIND);
1246 			if (map == NULL)
1247 				syserr("554 5.3.0 rewrite: map %s not found", mapname);
1248 
1249 			/* extract the match part */
1250 			key_rvp = ++rvp;
1251 			default_rvp = NULL;
1252 			arg_rvp = argvect;
1253 			xpvp = NULL;
1254 			replac = pvpbuf;
1255 			while (*rvp != NULL && (**rvp & 0377) != endtoken)
1256 			{
1257 				int nodetype = **rvp & 0377;
1258 
1259 				if (nodetype != CANONHOST && nodetype != CANONUSER)
1260 				{
1261 					rvp++;
1262 					continue;
1263 				}
1264 
1265 				*rvp++ = NULL;
1266 
1267 				if (xpvp != NULL)
1268 				{
1269 					cataddr(xpvp, NULL, replac,
1270 						&pvpbuf[sizeof pvpbuf] - replac,
1271 						'\0');
1272 					*++arg_rvp = replac;
1273 					replac += strlen(replac) + 1;
1274 					xpvp = NULL;
1275 				}
1276 				switch (nodetype)
1277 				{
1278 				  case CANONHOST:
1279 					xpvp = rvp;
1280 					break;
1281 
1282 				  case CANONUSER:
1283 					default_rvp = rvp;
1284 					break;
1285 				}
1286 			}
1287 			if (*rvp != NULL)
1288 				*rvp++ = NULL;
1289 			if (xpvp != NULL)
1290 			{
1291 				cataddr(xpvp, NULL, replac,
1292 					&pvpbuf[sizeof pvpbuf] - replac,
1293 					'\0');
1294 				*++arg_rvp = replac;
1295 			}
1296 			*++arg_rvp = NULL;
1297 
1298 			/* save the remainder of the input string */
1299 			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1300 			memmove((char *) pvpb1, (char *) rvp, trsize);
1301 
1302 			/* look it up */
1303 			cataddr(key_rvp, NULL, cbuf, sizeof cbuf,
1304 				map == NULL ? '\0' : map->s_map.map_spacesub);
1305 			argvect[0] = cbuf;
1306 			replac = map_lookup(map, cbuf, argvect, &rstat, e);
1307 
1308 			/* if no replacement, use default */
1309 			if (replac == NULL && default_rvp != NULL)
1310 			{
1311 				/* create the default */
1312 				cataddr(default_rvp, NULL, cbuf, sizeof cbuf, '\0');
1313 				replac = cbuf;
1314 			}
1315 
1316 			if (replac == NULL)
1317 			{
1318 				xpvp = key_rvp;
1319 			}
1320 			else if (*replac == '\0')
1321 			{
1322 				/* null replacement */
1323 				nullpvp[0] = NULL;
1324 				xpvp = nullpvp;
1325 			}
1326 			else
1327 			{
1328 				/* scan the new replacement */
1329 				xpvp = prescan(replac, '\0', pvpbuf,
1330 					       sizeof pvpbuf, NULL, NULL);
1331 				if (xpvp == NULL)
1332 				{
1333 					/* prescan already printed error */
1334 					return EX_DATAERR;
1335 				}
1336 			}
1337 
1338 			/* append it to the token list */
1339 			for (avp = hbrvp; *xpvp != NULL; xpvp++)
1340 			{
1341 				*avp++ = newstr(*xpvp);
1342 				if (avp >= &npvp[MAXATOM])
1343 					goto toolong;
1344 			}
1345 
1346 			/* restore the old trailing information */
1347 			rvp = avp - 1;
1348 			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1349 				if (avp >= &npvp[MAXATOM])
1350 					goto toolong;
1351 		}
1352 
1353 		/*
1354 		**  Check for subroutine calls.
1355 		*/
1356 
1357 		status = callsubr(npvp, reclevel, e);
1358 		if (rstat == EX_OK || status == EX_TEMPFAIL)
1359 			rstat = status;
1360 
1361 		/* copy vector back into original space. */
1362 		for (avp = npvp; *avp++ != NULL;)
1363 			continue;
1364 		memmove((char *) pvp, (char *) npvp,
1365 		      (int) (avp - npvp) * sizeof *avp);
1366 
1367 		if (tTd(21, 4))
1368 		{
1369 			dprintf("rewritten as:");
1370 			printav(pvp);
1371 		}
1372 	}
1373 
1374 	if (OpMode == MD_TEST)
1375 	{
1376 		printf("%s%-16.16s returns:", prefix, rulename);
1377 		printav(pvp);
1378 	}
1379 	else if (tTd(21, 1))
1380 	{
1381 		dprintf("%s%-16.16s returns:", prefix, rulename);
1382 		printav(pvp);
1383 	}
1384 	return rstat;
1385 }
1386 /*
1387 **  CALLSUBR -- call subroutines in rewrite vector
1388 **
1389 **	Parameters:
1390 **		pvp -- pointer to token vector.
1391 **		reclevel -- the current recursion level.
1392 **		e -- the current envelope.
1393 **
1394 **	Returns:
1395 **		The status from the subroutine call.
1396 **
1397 **	Side Effects:
1398 **		pvp is modified.
1399 */
1400 
1401 static int
1402 callsubr(pvp, reclevel, e)
1403 	char **pvp;
1404 	int reclevel;
1405 	ENVELOPE *e;
1406 {
1407 	char **avp;
1408 	char **rvp;
1409 	register int i;
1410 	int subr;
1411 	int status;
1412 	int rstat = EX_OK;
1413 	char *tpvp[MAXATOM + 1];
1414 
1415 	for (avp = pvp; *avp != NULL; avp++)
1416 	{
1417 		if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
1418 		{
1419 			stripquotes(avp[1]);
1420 			subr = strtorwset(avp[1], NULL, ST_FIND);
1421 			if (subr < 0)
1422 			{
1423 				syserr("Unknown ruleset %s", avp[1]);
1424 				return EX_CONFIG;
1425 			}
1426 
1427 			if (tTd(21, 3))
1428 				dprintf("-----callsubr %s (%d)\n",
1429 					avp[1], subr);
1430 
1431 			/*
1432 			**  Take care of possible inner calls first.
1433 			**  use a full size temporary buffer to avoid
1434 			**  overflows in rewrite, but strip off the
1435 			**  subroutine call.
1436 			*/
1437 
1438 			for (i = 2; avp[i] != NULL; i++)
1439 				tpvp[i - 2] = avp[i];
1440 			tpvp[i - 2] = NULL;
1441 
1442 			status = callsubr(tpvp, reclevel, e);
1443 			if (rstat == EX_OK || status == EX_TEMPFAIL)
1444 				rstat = status;
1445 
1446 			/*
1447 			**  Now we need to call the ruleset specified for
1448 			**  the subroutine. we can do this with the
1449 			**  temporary buffer that we set up earlier,
1450 			**  since it has all the data we want to rewrite.
1451 			*/
1452 
1453 			status = rewrite(tpvp, subr, reclevel, e);
1454 			if (rstat == EX_OK || status == EX_TEMPFAIL)
1455 				rstat = status;
1456 
1457 			/*
1458 			**  Find length of tpvp and current offset into
1459 			**  pvp, if the total is greater than MAXATOM,
1460 			**  then it would overflow the buffer if we copied
1461 			**  it back in to pvp, in which case we throw a
1462 			**  fit.
1463 			*/
1464 
1465 			for (rvp = tpvp; *rvp != NULL; rvp++)
1466 				continue;
1467 			if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
1468 			{
1469 				syserr("554 5.3.0 callsubr: expansion too long");
1470 				return EX_DATAERR;
1471 			}
1472 
1473 			/*
1474 			**  Now we can copy the rewritten code over
1475 			**  the initial subroutine call in the buffer.
1476 			*/
1477 
1478 			for (i = 0; tpvp[i] != NULL; i++)
1479 				avp[i] = tpvp[i];
1480 			avp[i] = NULL;
1481 
1482 			/*
1483 			**  If we got this far, we've processed the left
1484 			**  most subroutine, and recursively called ourselves
1485 			**  to handle any other subroutines.  We're done.
1486 			*/
1487 
1488 			break;
1489 		}
1490 	}
1491 	return rstat;
1492 }
1493 /*
1494 **  MAP_LOOKUP -- do lookup in map
1495 **
1496 **	Parameters:
1497 **		map -- the map to use for the lookup.
1498 **		key -- the key to look up.
1499 **		argvect -- arguments to pass to the map lookup.
1500 **		pstat -- a pointer to an integer in which to store the
1501 **			status from the lookup.
1502 **		e -- the current envelope.
1503 **
1504 **	Returns:
1505 **		The result of the lookup.
1506 **		NULL -- if there was no data for the given key.
1507 */
1508 
1509 static char *
1510 map_lookup(smap, key, argvect, pstat, e)
1511 	STAB *smap;
1512 	char key[];
1513 	char **argvect;
1514 	int *pstat;
1515 	ENVELOPE *e;
1516 {
1517 	auto int status = EX_OK;
1518 	MAP *map;
1519 	char *replac;
1520 
1521 	if (smap == NULL)
1522 		return NULL;
1523 
1524 	map = &smap->s_map;
1525 	DYNOPENMAP(map);
1526 
1527 	if (e->e_sendmode == SM_DEFER &&
1528 	    bitset(MF_DEFER, map->map_mflags))
1529 	{
1530 		/* don't do any map lookups */
1531 		if (tTd(60, 1))
1532 			dprintf("map_lookup(%s, %s) => DEFERRED\n",
1533 				smap->s_name, key);
1534 		*pstat = EX_TEMPFAIL;
1535 		return NULL;
1536 	}
1537 
1538 	if (!bitset(MF_KEEPQUOTES, map->map_mflags))
1539 		stripquotes(key);
1540 
1541 	if (tTd(60, 1))
1542 	{
1543 		dprintf("map_lookup(%s, %s", smap->s_name, key);
1544 		if (tTd(60, 5))
1545 		{
1546 			int i;
1547 
1548 			for (i = 0; argvect[i] != NULL; i++)
1549 				dprintf(", %%%d=%s", i, argvect[i]);
1550 		}
1551 		dprintf(") => ");
1552 	}
1553 	replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
1554 	if (tTd(60, 1))
1555 		dprintf("%s (%d)\n",
1556 			replac != NULL ? replac : "NOT FOUND",
1557 			status);
1558 
1559 	/* should recover if status == EX_TEMPFAIL */
1560 	if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
1561 	{
1562 		*pstat = EX_TEMPFAIL;
1563 		if (tTd(60, 1))
1564 			dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
1565 				smap->s_name, key, errno);
1566 		if (e->e_message == NULL)
1567 		{
1568 			char mbuf[320];
1569 
1570 			snprintf(mbuf, sizeof mbuf,
1571 				"%.80s map: lookup (%s): deferred",
1572 				smap->s_name,
1573 				shortenstring(key, MAXSHORTSTR));
1574 			e->e_message = newstr(mbuf);
1575 		}
1576 	}
1577 	if (status == EX_TEMPFAIL && map->map_tapp != NULL)
1578 	{
1579 		size_t i = strlen(key) + strlen(map->map_tapp) + 1;
1580 		static char *rwbuf = NULL;
1581 		static size_t rwbuflen = 0;
1582 
1583 		if (i > rwbuflen)
1584 		{
1585 			if (rwbuf != NULL)
1586 				sm_free(rwbuf);
1587 			rwbuflen = i;
1588 			rwbuf = (char *) xalloc(rwbuflen);
1589 		}
1590 		snprintf(rwbuf, rwbuflen, "%s%s", key, map->map_tapp);
1591 		if (tTd(60, 4))
1592 			dprintf("map_lookup tempfail: returning \"%s\"\n",
1593 				rwbuf);
1594 		return rwbuf;
1595 	}
1596 	return replac;
1597 }
1598 /*
1599 **  INITERRMAILERS -- initialize error and discard mailers
1600 **
1601 **	Parameters:
1602 **		none.
1603 **
1604 **	Returns:
1605 **		none.
1606 **
1607 **	Side Effects:
1608 **		initializes error and discard mailers.
1609 */
1610 
1611 static MAILER discardmailer;
1612 static MAILER errormailer;
1613 static char *discardargv[] = { "DISCARD", NULL };
1614 static char *errorargv[] = { "ERROR", NULL };
1615 
1616 void
1617 initerrmailers()
1618 {
1619 	if (discardmailer.m_name == NULL)
1620 	{
1621 		/* initialize the discard mailer */
1622 		discardmailer.m_name = "*discard*";
1623 		discardmailer.m_mailer = "DISCARD";
1624 		discardmailer.m_argv = discardargv;
1625 	}
1626 	if (errormailer.m_name == NULL)
1627 	{
1628 		/* initialize the bogus mailer */
1629 		errormailer.m_name = "*error*";
1630 		errormailer.m_mailer = "ERROR";
1631 		errormailer.m_argv = errorargv;
1632 	}
1633 }
1634 /*
1635 **  BUILDADDR -- build address from token vector.
1636 **
1637 **	Parameters:
1638 **		tv -- token vector.
1639 **		a -- pointer to address descriptor to fill.
1640 **			If NULL, one will be allocated.
1641 **		flags -- info regarding whether this is a sender or
1642 **			a recipient.
1643 **		e -- the current envelope.
1644 **
1645 **	Returns:
1646 **		NULL if there was an error.
1647 **		'a' otherwise.
1648 **
1649 **	Side Effects:
1650 **		fills in 'a'
1651 */
1652 
1653 static struct errcodes
1654 {
1655 	char	*ec_name;		/* name of error code */
1656 	int	ec_code;		/* numeric code */
1657 } ErrorCodes[] =
1658 {
1659 	{ "usage",		EX_USAGE	},
1660 	{ "nouser",		EX_NOUSER	},
1661 	{ "nohost",		EX_NOHOST	},
1662 	{ "unavailable",	EX_UNAVAILABLE	},
1663 	{ "software",		EX_SOFTWARE	},
1664 	{ "tempfail",		EX_TEMPFAIL	},
1665 	{ "protocol",		EX_PROTOCOL	},
1666 #ifdef EX_CONFIG
1667 	{ "config",		EX_CONFIG	},
1668 #endif /* EX_CONFIG */
1669 	{ NULL,			EX_UNAVAILABLE	}
1670 };
1671 
1672 
1673 static ADDRESS *
1674 buildaddr(tv, a, flags, e)
1675 	register char **tv;
1676 	register ADDRESS *a;
1677 	int flags;
1678 	register ENVELOPE *e;
1679 {
1680 	struct mailer **mp;
1681 	register struct mailer *m;
1682 	register char *p;
1683 	char *mname;
1684 	char **hostp;
1685 	char hbuf[MAXNAME + 1];
1686 	static char ubuf[MAXNAME + 2];
1687 
1688 	if (tTd(24, 5))
1689 	{
1690 		dprintf("buildaddr, flags=%x, tv=", flags);
1691 		printav(tv);
1692 	}
1693 
1694 	if (a == NULL)
1695 		a = (ADDRESS *) xalloc(sizeof *a);
1696 	memset((char *) a, '\0', sizeof *a);
1697 	hbuf[0] = '\0';
1698 
1699 	/* set up default error return flags */
1700 	a->q_flags |= DefaultNotify;
1701 
1702 	/* figure out what net/mailer to use */
1703 	if (*tv == NULL || (**tv & 0377) != CANONNET)
1704 	{
1705 		syserr("554 5.3.5 buildaddr: no mailer in parsed address");
1706 badaddr:
1707 		if (ExitStat == EX_TEMPFAIL)
1708 			a->q_state = QS_QUEUEUP;
1709 		else
1710 		{
1711 			a->q_state = QS_BADADDR;
1712 			a->q_mailer = &errormailer;
1713 		}
1714 		return a;
1715 	}
1716 	mname = *++tv;
1717 
1718 	/* extract host and user portions */
1719 	if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1720 		hostp = ++tv;
1721 	else
1722 		hostp = NULL;
1723 	while (*tv != NULL && (**tv & 0377) != CANONUSER)
1724 		tv++;
1725 	if (*tv == NULL)
1726 	{
1727 		syserr("554 5.3.5 buildaddr: no user");
1728 		goto badaddr;
1729 	}
1730 	if (tv == hostp)
1731 		hostp = NULL;
1732 	else if (hostp != NULL)
1733 		cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1734 	cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1735 
1736 	/* save away the host name */
1737 	if (strcasecmp(mname, "error") == 0)
1738 	{
1739 		/* Set up triplet for use by -bv */
1740 		a->q_mailer = &errormailer;
1741 		a->q_user = newstr(ubuf);
1742 
1743 		if (hostp != NULL)
1744 		{
1745 			register struct errcodes *ep;
1746 
1747 			a->q_host = newstr(hbuf);
1748 			if (strchr(hbuf, '.') != NULL)
1749 			{
1750 				a->q_status = newstr(hbuf);
1751 				setstat(dsntoexitstat(hbuf));
1752 			}
1753 			else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
1754 			{
1755 				setstat(atoi(hbuf));
1756 			}
1757 			else
1758 			{
1759 				for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1760 					if (strcasecmp(ep->ec_name, hbuf) == 0)
1761 						break;
1762 				setstat(ep->ec_code);
1763 			}
1764 		}
1765 		else
1766 		{
1767 			a->q_host = NULL;
1768 			setstat(EX_UNAVAILABLE);
1769 		}
1770 		stripquotes(ubuf);
1771 		if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
1772 		{
1773 			char fmt[16];
1774 			int off;
1775 
1776 			if ((off = isenhsc(ubuf + 4, ' ')) > 0)
1777 			{
1778 				ubuf[off + 4] = '\0';
1779 				off += 5;
1780 			}
1781 			else
1782 			{
1783 				off = 4;
1784 				ubuf[3] = '\0';
1785 			}
1786 			(void) snprintf(fmt, sizeof fmt, "%s %%s", ubuf);
1787 			if (off > 4)
1788 				usrerr(fmt, ubuf + off);
1789 			else if (isenhsc(hbuf, '\0') > 0)
1790 				usrerrenh(hbuf, fmt, ubuf + off);
1791 			else
1792 				usrerr(fmt, ubuf + off);
1793 			/* XXX ubuf[off - 1] = ' '; */
1794 		}
1795 		else
1796 		{
1797 			usrerr("553 5.3.0 %s", ubuf);
1798 		}
1799 		goto badaddr;
1800 	}
1801 
1802 	for (mp = Mailer; (m = *mp++) != NULL; )
1803 	{
1804 		if (strcasecmp(m->m_name, mname) == 0)
1805 			break;
1806 	}
1807 	if (m == NULL)
1808 	{
1809 		syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
1810 		goto badaddr;
1811 	}
1812 	a->q_mailer = m;
1813 
1814 	/* figure out what host (if any) */
1815 	if (hostp == NULL)
1816 	{
1817 		if (!bitnset(M_LOCALMAILER, m->m_flags))
1818 		{
1819 			syserr("554 5.3.5 buildaddr: no host");
1820 			goto badaddr;
1821 		}
1822 		a->q_host = NULL;
1823 	}
1824 	else
1825 		a->q_host = newstr(hbuf);
1826 
1827 	/* figure out the user */
1828 	p = ubuf;
1829 	if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
1830 	{
1831 		p++;
1832 		tv++;
1833 		a->q_flags |= QNOTREMOTE;
1834 	}
1835 
1836 	/* do special mapping for local mailer */
1837 	if (*p == '"')
1838 		p++;
1839 	if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1840 		a->q_mailer = m = ProgMailer;
1841 	else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1842 		a->q_mailer = m = FileMailer;
1843 	else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
1844 	{
1845 		/* may be :include: */
1846 		stripquotes(ubuf);
1847 		if (strncasecmp(ubuf, ":include:", 9) == 0)
1848 		{
1849 			/* if :include:, don't need further rewriting */
1850 			a->q_mailer = m = InclMailer;
1851 			a->q_user = newstr(&ubuf[9]);
1852 			return a;
1853 		}
1854 	}
1855 
1856 	/* rewrite according recipient mailer rewriting rules */
1857 	define('h', a->q_host, e);
1858 
1859 #if _FFR_ADDR_TYPE
1860 	/*
1861 	**  Note, change the 9 to a 10 before removing #if FFR check
1862 	**  in a future version.
1863 	*/
1864 
1865 	if (ConfigLevel >= 9 ||
1866 	    !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1867 #else /* _FFR_ADDR_TYPE */
1868 	if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1869 #endif /* _FFR_ADDR_TYPE */
1870 	{
1871 		/* sender addresses done later */
1872 		(void) rewrite(tv, 2, 0, e);
1873 		if (m->m_re_rwset > 0)
1874 		       (void) rewrite(tv, m->m_re_rwset, 0, e);
1875 	}
1876 	(void) rewrite(tv, 4, 0, e);
1877 
1878 	/* save the result for the command line/RCPT argument */
1879 	cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
1880 	a->q_user = newstr(ubuf);
1881 
1882 	/*
1883 	**  Do mapping to lower case as requested by mailer
1884 	*/
1885 
1886 	if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1887 		makelower(a->q_host);
1888 	if (!bitnset(M_USR_UPPER, m->m_flags))
1889 		makelower(a->q_user);
1890 
1891 	if (tTd(24, 6))
1892 	{
1893 		dprintf("buildaddr => ");
1894 		printaddr(a, FALSE);
1895 	}
1896 	return a;
1897 }
1898 /*
1899 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1900 **
1901 **	Parameters:
1902 **		pvp -- parameter vector to rebuild.
1903 **		evp -- last parameter to include.  Can be NULL to
1904 **			use entire pvp.
1905 **		buf -- buffer to build the string into.
1906 **		sz -- size of buf.
1907 **		spacesub -- the space separator character; if null,
1908 **			use SpaceSub.
1909 **
1910 **	Returns:
1911 **		none.
1912 **
1913 **	Side Effects:
1914 **		Destroys buf.
1915 */
1916 
1917 void
1918 cataddr(pvp, evp, buf, sz, spacesub)
1919 	char **pvp;
1920 	char **evp;
1921 	char *buf;
1922 	register int sz;
1923 	int spacesub;
1924 {
1925 	bool oatomtok = FALSE;
1926 	bool natomtok = FALSE;
1927 	register int i;
1928 	register char *p;
1929 
1930 	if (sz <= 0)
1931 		return;
1932 
1933 	if (spacesub == '\0')
1934 		spacesub = SpaceSub;
1935 
1936 	if (pvp == NULL)
1937 	{
1938 		*buf = '\0';
1939 		return;
1940 	}
1941 	p = buf;
1942 	sz -= 2;
1943 	while (*pvp != NULL && (i = strlen(*pvp)) < sz - 1)
1944 	{
1945 		natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
1946 		if (oatomtok && natomtok)
1947 		{
1948 			*p++ = spacesub;
1949 			--sz;
1950 		}
1951 		(void) strlcpy(p, *pvp, sz);
1952 		oatomtok = natomtok;
1953 		p += i;
1954 		sz -= i;
1955 		if (pvp++ == evp)
1956 			break;
1957 	}
1958 	*p = '\0';
1959 }
1960 /*
1961 **  SAMEADDR -- Determine if two addresses are the same
1962 **
1963 **	This is not just a straight comparison -- if the mailer doesn't
1964 **	care about the host we just ignore it, etc.
1965 **
1966 **	Parameters:
1967 **		a, b -- pointers to the internal forms to compare.
1968 **
1969 **	Returns:
1970 **		TRUE -- they represent the same mailbox.
1971 **		FALSE -- they don't.
1972 **
1973 **	Side Effects:
1974 **		none.
1975 */
1976 
1977 bool
1978 sameaddr(a, b)
1979 	register ADDRESS *a;
1980 	register ADDRESS *b;
1981 {
1982 	register ADDRESS *ca, *cb;
1983 
1984 	/* if they don't have the same mailer, forget it */
1985 	if (a->q_mailer != b->q_mailer)
1986 		return FALSE;
1987 
1988 	/* if the user isn't the same, we can drop out */
1989 	if (strcmp(a->q_user, b->q_user) != 0)
1990 		return FALSE;
1991 
1992 	/* if we have good uids for both but they differ, these are different */
1993 	if (a->q_mailer == ProgMailer)
1994 	{
1995 		ca = getctladdr(a);
1996 		cb = getctladdr(b);
1997 		if (ca != NULL && cb != NULL &&
1998 		    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1999 		    ca->q_uid != cb->q_uid)
2000 			return FALSE;
2001 	}
2002 
2003 	/* otherwise compare hosts (but be careful for NULL ptrs) */
2004 	if (a->q_host == b->q_host)
2005 	{
2006 		/* probably both null pointers */
2007 		return TRUE;
2008 	}
2009 	if (a->q_host == NULL || b->q_host == NULL)
2010 	{
2011 		/* only one is a null pointer */
2012 		return FALSE;
2013 	}
2014 	if (strcmp(a->q_host, b->q_host) != 0)
2015 		return FALSE;
2016 
2017 	return TRUE;
2018 }
2019 /*
2020 **  PRINTADDR -- print address (for debugging)
2021 **
2022 **	Parameters:
2023 **		a -- the address to print
2024 **		follow -- follow the q_next chain.
2025 **
2026 **	Returns:
2027 **		none.
2028 **
2029 **	Side Effects:
2030 **		none.
2031 */
2032 
2033 struct qflags
2034 {
2035 	char	*qf_name;
2036 	u_long	qf_bit;
2037 };
2038 
2039 static struct qflags	AddressFlags[] =
2040 {
2041 	{ "QGOODUID",		QGOODUID	},
2042 	{ "QPRIMARY",		QPRIMARY	},
2043 	{ "QNOTREMOTE",		QNOTREMOTE	},
2044 	{ "QSELFREF",		QSELFREF	},
2045 	{ "QBOGUSSHELL",	QBOGUSSHELL	},
2046 	{ "QUNSAFEADDR",	QUNSAFEADDR	},
2047 	{ "QPINGONSUCCESS",	QPINGONSUCCESS	},
2048 	{ "QPINGONFAILURE",	QPINGONFAILURE	},
2049 	{ "QPINGONDELAY",	QPINGONDELAY	},
2050 	{ "QHASNOTIFY",		QHASNOTIFY	},
2051 	{ "QRELAYED",		QRELAYED	},
2052 	{ "QEXPANDED",		QEXPANDED	},
2053 	{ "QDELIVERED",		QDELIVERED	},
2054 	{ "QDELAYED",		QDELAYED	},
2055 	{ "QTHISPASS",		QTHISPASS	},
2056 	{ "QRCPTOK",		QRCPTOK		},
2057 	{ NULL,			0		}
2058 };
2059 
2060 void
2061 printaddr(a, follow)
2062 	register ADDRESS *a;
2063 	bool follow;
2064 {
2065 	register MAILER *m;
2066 	MAILER pseudomailer;
2067 	register struct qflags *qfp;
2068 	bool firstone;
2069 
2070 	if (a == NULL)
2071 	{
2072 		printf("[NULL]\n");
2073 		return;
2074 	}
2075 
2076 	while (a != NULL)
2077 	{
2078 		printf("%lx=", (u_long) a);
2079 		(void) fflush(stdout);
2080 
2081 		/* find the mailer -- carefully */
2082 		m = a->q_mailer;
2083 		if (m == NULL)
2084 		{
2085 			m = &pseudomailer;
2086 			m->m_mno = -1;
2087 			m->m_name = "NULL";
2088 		}
2089 
2090 		printf("%s:\n\tmailer %d (%s), host `%s'\n",
2091 		       a->q_paddr == NULL ? "<null>" : a->q_paddr,
2092 		       m->m_mno, m->m_name,
2093 		       a->q_host == NULL ? "<null>" : a->q_host);
2094 		printf("\tuser `%s', ruser `%s'\n",
2095 		       a->q_user,
2096 		       a->q_ruser == NULL ? "<null>" : a->q_ruser);
2097 		printf("\tstate=");
2098 		switch (a->q_state)
2099 		{
2100 		  case QS_OK:
2101 			printf("OK");
2102 			break;
2103 
2104 		  case QS_DONTSEND:
2105 			printf("DONTSEND");
2106 			break;
2107 
2108 		  case QS_BADADDR:
2109 			printf("BADADDR");
2110 			break;
2111 
2112 		  case QS_QUEUEUP:
2113 			printf("QUEUEUP");
2114 			break;
2115 
2116 		  case QS_SENT:
2117 			printf("SENT");
2118 			break;
2119 
2120 		  case QS_VERIFIED:
2121 			printf("VERIFIED");
2122 			break;
2123 
2124 		  case QS_EXPANDED:
2125 			printf("EXPANDED");
2126 			break;
2127 
2128 		  case QS_SENDER:
2129 			printf("SENDER");
2130 			break;
2131 
2132 		  case QS_CLONED:
2133 			printf("CLONED");
2134 			break;
2135 
2136 		  case QS_DISCARDED:
2137 			printf("DISCARDED");
2138 			break;
2139 
2140 		  case QS_REPLACED:
2141 			printf("REPLACED");
2142 			break;
2143 
2144 		  case QS_REMOVED:
2145 			printf("REMOVED");
2146 			break;
2147 
2148 		  case QS_DUPLICATE:
2149 			printf("DUPLICATE");
2150 			break;
2151 
2152 		  case QS_INCLUDED:
2153 			printf("INCLUDED");
2154 			break;
2155 
2156 		  default:
2157 			printf("%d", a->q_state);
2158 			break;
2159 		}
2160 		printf(", next=%lx, alias %lx, uid %d, gid %d\n",
2161 		       (u_long) a->q_next, (u_long) a->q_alias,
2162 		       (int) a->q_uid, (int) a->q_gid);
2163 		printf("\tflags=%lx<", a->q_flags);
2164 		firstone = TRUE;
2165 		for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
2166 		{
2167 			if (!bitset(qfp->qf_bit, a->q_flags))
2168 				continue;
2169 			if (!firstone)
2170 				printf(",");
2171 			firstone = FALSE;
2172 			printf("%s", qfp->qf_name);
2173 		}
2174 		printf(">\n");
2175 		printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
2176 		       a->q_owner == NULL ? "(none)" : a->q_owner,
2177 		       a->q_home == NULL ? "(none)" : a->q_home,
2178 		       a->q_fullname == NULL ? "(none)" : a->q_fullname);
2179 		printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
2180 		       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
2181 		       a->q_statmta == NULL ? "(none)" : a->q_statmta,
2182 		       a->q_status == NULL ? "(none)" : a->q_status);
2183 		printf("\trstatus=\"%s\"\n",
2184 		       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2185 		printf("\tspecificity=%d, statdate=%s\n",
2186 		       a->q_specificity,
2187 		       a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
2188 
2189 		if (!follow)
2190 			return;
2191 		a = a->q_next;
2192 	}
2193 }
2194 /*
2195 **  EMPTYADDR -- return TRUE if this address is empty (``<>'')
2196 **
2197 **	Parameters:
2198 **		a -- pointer to the address
2199 **
2200 **	Returns:
2201 **		TRUE -- if this address is "empty" (i.e., no one should
2202 **			ever generate replies to it.
2203 **		FALSE -- if it is a "regular" (read: replyable) address.
2204 */
2205 
2206 bool
2207 emptyaddr(a)
2208 	register ADDRESS *a;
2209 {
2210 	return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2211 	       a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2212 }
2213 /*
2214 **  REMOTENAME -- return the name relative to the current mailer
2215 **
2216 **	Parameters:
2217 **		name -- the name to translate.
2218 **		m -- the mailer that we want to do rewriting relative
2219 **			to.
2220 **		flags -- fine tune operations.
2221 **		pstat -- pointer to status word.
2222 **		e -- the current envelope.
2223 **
2224 **	Returns:
2225 **		the text string representing this address relative to
2226 **			the receiving mailer.
2227 **
2228 **	Side Effects:
2229 **		none.
2230 **
2231 **	Warnings:
2232 **		The text string returned is tucked away locally;
2233 **			copy it if you intend to save it.
2234 */
2235 
2236 char *
2237 remotename(name, m, flags, pstat, e)
2238 	char *name;
2239 	struct mailer *m;
2240 	int flags;
2241 	int *pstat;
2242 	register ENVELOPE *e;
2243 {
2244 	register char **pvp;
2245 	char *fancy;
2246 	char *oldg = macvalue('g', e);
2247 	int rwset;
2248 	static char buf[MAXNAME + 1];
2249 	char lbuf[MAXNAME + 1];
2250 	char pvpbuf[PSBUFSIZE];
2251 #if _FFR_ADDR_TYPE
2252 	char addrtype[4];
2253 #endif /* _FFR_ADDR_TYPE */
2254 
2255 	if (tTd(12, 1))
2256 		dprintf("remotename(%s)\n", name);
2257 
2258 	/* don't do anything if we are tagging it as special */
2259 	if (bitset(RF_SENDERADDR, flags))
2260 	{
2261 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2262 						     : m->m_se_rwset;
2263 #if _FFR_ADDR_TYPE
2264 		addrtype[2] = 's';
2265 #endif /* _FFR_ADDR_TYPE */
2266 	}
2267 	else
2268 	{
2269 		rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2270 						     : m->m_re_rwset;
2271 #if _FFR_ADDR_TYPE
2272 		addrtype[2] = 'r';
2273 #endif /* _FFR_ADDR_TYPE */
2274 	}
2275 	if (rwset < 0)
2276 		return name;
2277 #if _FFR_ADDR_TYPE
2278 	addrtype[1] = ' ';
2279 	addrtype[3] = '\0';
2280 	addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
2281 	define(macid("{addr_type}", NULL), addrtype, e);
2282 #endif /* _FFR_ADDR_TYPE */
2283 
2284 	/*
2285 	**  Do a heuristic crack of this name to extract any comment info.
2286 	**	This will leave the name as a comment and a $g macro.
2287 	*/
2288 
2289 	if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2290 		fancy = "\201g";
2291 	else
2292 		fancy = crackaddr(name);
2293 
2294 	/*
2295 	**  Turn the name into canonical form.
2296 	**	Normally this will be RFC 822 style, i.e., "user@domain".
2297 	**	If this only resolves to "user", and the "C" flag is
2298 	**	specified in the sending mailer, then the sender's
2299 	**	domain will be appended.
2300 	*/
2301 
2302 	pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2303 	if (pvp == NULL)
2304 		return name;
2305 	if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2306 		*pstat = EX_TEMPFAIL;
2307 	if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2308 	{
2309 		/* append from domain to this address */
2310 		register char **pxp = pvp;
2311 		int l = MAXATOM;	/* size of buffer for pvp */
2312 
2313 		/* see if there is an "@domain" in the current name */
2314 		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2315 		{
2316 			pxp++;
2317 			--l;
2318 		}
2319 		if (*pxp == NULL)
2320 		{
2321 			/* no.... append the "@domain" from the sender */
2322 			register char **qxq = e->e_fromdomain;
2323 
2324 			while ((*pxp++ = *qxq++) != NULL)
2325 			{
2326 				if (--l <= 0)
2327 				{
2328 					*--pxp = NULL;
2329 					usrerr("553 5.1.0 remotename: too many tokens");
2330 					*pstat = EX_UNAVAILABLE;
2331 					break;
2332 				}
2333 			}
2334 			if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2335 				*pstat = EX_TEMPFAIL;
2336 		}
2337 	}
2338 
2339 	/*
2340 	**  Do more specific rewriting.
2341 	**	Rewrite using ruleset 1 or 2 depending on whether this is
2342 	**		a sender address or not.
2343 	**	Then run it through any receiving-mailer-specific rulesets.
2344 	*/
2345 
2346 	if (bitset(RF_SENDERADDR, flags))
2347 	{
2348 		if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
2349 			*pstat = EX_TEMPFAIL;
2350 	}
2351 	else
2352 	{
2353 		if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
2354 			*pstat = EX_TEMPFAIL;
2355 	}
2356 	if (rwset > 0)
2357 	{
2358 		if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
2359 			*pstat = EX_TEMPFAIL;
2360 	}
2361 
2362 	/*
2363 	**  Do any final sanitation the address may require.
2364 	**	This will normally be used to turn internal forms
2365 	**	(e.g., user@host.LOCAL) into external form.  This
2366 	**	may be used as a default to the above rules.
2367 	*/
2368 
2369 	if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
2370 		*pstat = EX_TEMPFAIL;
2371 
2372 	/*
2373 	**  Now restore the comment information we had at the beginning.
2374 	*/
2375 
2376 	cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
2377 	define('g', lbuf, e);
2378 
2379 	/* need to make sure route-addrs have <angle brackets> */
2380 	if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2381 		expand("<\201g>", buf, sizeof buf, e);
2382 	else
2383 		expand(fancy, buf, sizeof buf, e);
2384 
2385 	define('g', oldg, e);
2386 
2387 	if (tTd(12, 1))
2388 		dprintf("remotename => `%s'\n", buf);
2389 	return buf;
2390 }
2391 /*
2392 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2393 **
2394 **	Parameters:
2395 **		a -- the address to map (but just the user name part).
2396 **		sendq -- the sendq in which to install any replacement
2397 **			addresses.
2398 **		aliaslevel -- the alias nesting depth.
2399 **		e -- the envelope.
2400 **
2401 **	Returns:
2402 **		none.
2403 */
2404 
2405 #define Q_COPYFLAGS	(QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2406 			 Q_PINGFLAGS|QHASNOTIFY|\
2407 			 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
2408 
2409 void
2410 maplocaluser(a, sendq, aliaslevel, e)
2411 	register ADDRESS *a;
2412 	ADDRESS **sendq;
2413 	int aliaslevel;
2414 	ENVELOPE *e;
2415 {
2416 	register char **pvp;
2417 	register ADDRESS *a1 = NULL;
2418 	auto char *delimptr;
2419 	char pvpbuf[PSBUFSIZE];
2420 
2421 	if (tTd(29, 1))
2422 	{
2423 		dprintf("maplocaluser: ");
2424 		printaddr(a, FALSE);
2425 	}
2426 	pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
2427 	if (pvp == NULL)
2428 	{
2429 		if (tTd(29, 9))
2430 			dprintf("maplocaluser: cannot prescan %s\n",
2431 				a->q_user);
2432 		return;
2433 	}
2434 
2435 	define('h', a->q_host, e);
2436 	define('u', a->q_user, e);
2437 	define('z', a->q_home, e);
2438 
2439 #if _FFR_ADDR_TYPE
2440 	define(macid("{addr_type}", NULL), "e r", e);
2441 #endif /* _FFR_ADDR_TYPE */
2442 	if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
2443 	{
2444 		if (tTd(29, 9))
2445 			dprintf("maplocaluser: rewrite tempfail\n");
2446 		a->q_state = QS_QUEUEUP;
2447 		a->q_status = "4.4.3";
2448 		return;
2449 	}
2450 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2451 	{
2452 		if (tTd(29, 9))
2453 			dprintf("maplocaluser: doesn't resolve\n");
2454 		return;
2455 	}
2456 
2457 	/* if non-null, mailer destination specified -- has it changed? */
2458 	a1 = buildaddr(pvp, NULL, 0, e);
2459 	if (a1 == NULL || sameaddr(a, a1))
2460 	{
2461 		if (tTd(29, 9))
2462 			dprintf("maplocaluser: address unchanged\n");
2463 		if (a1 != NULL)
2464 			sm_free(a1);
2465 		return;
2466 	}
2467 
2468 	/* make new address take on flags and print attributes of old */
2469 	a1->q_flags &= ~Q_COPYFLAGS;
2470 	a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2471 	a1->q_paddr = newstr(a->q_paddr);
2472 	a1->q_orcpt = a->q_orcpt;
2473 
2474 	/* mark old address as dead; insert new address */
2475 	a->q_state = QS_REPLACED;
2476 	if (tTd(29, 5))
2477 	{
2478 		dprintf("maplocaluser: QS_REPLACED ");
2479 		printaddr(a, FALSE);
2480 	}
2481 	a1->q_alias = a;
2482 	allocaddr(a1, RF_COPYALL, newstr(a->q_paddr));
2483 	(void) recipient(a1, sendq, aliaslevel, e);
2484 }
2485 /*
2486 **  DEQUOTE_INIT -- initialize dequote map
2487 **
2488 **	This is a no-op.
2489 **
2490 **	Parameters:
2491 **		map -- the internal map structure.
2492 **		args -- arguments.
2493 **
2494 **	Returns:
2495 **		TRUE.
2496 */
2497 
2498 bool
2499 dequote_init(map, args)
2500 	MAP *map;
2501 	char *args;
2502 {
2503 	register char *p = args;
2504 
2505 	/* there is no check whether there is really an argument */
2506 	map->map_mflags |= MF_KEEPQUOTES;
2507 	for (;;)
2508 	{
2509 		while (isascii(*p) && isspace(*p))
2510 			p++;
2511 		if (*p != '-')
2512 			break;
2513 		switch (*++p)
2514 		{
2515 		  case 'a':
2516 			map->map_app = ++p;
2517 			break;
2518 
2519 		  case 'D':
2520 			map->map_mflags |= MF_DEFER;
2521 			break;
2522 
2523 		  case 'S':
2524 		  case 's':
2525 			map->map_spacesub = *++p;
2526 			break;
2527 		}
2528 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2529 			p++;
2530 		if (*p != '\0')
2531 			*p = '\0';
2532 	}
2533 	if (map->map_app != NULL)
2534 		map->map_app = newstr(map->map_app);
2535 
2536 	return TRUE;
2537 }
2538 /*
2539 **  DEQUOTE_MAP -- unquote an address
2540 **
2541 **	Parameters:
2542 **		map -- the internal map structure (ignored).
2543 **		name -- the name to dequote.
2544 **		av -- arguments (ignored).
2545 **		statp -- pointer to status out-parameter.
2546 **
2547 **	Returns:
2548 **		NULL -- if there were no quotes, or if the resulting
2549 **			unquoted buffer would not be acceptable to prescan.
2550 **		else -- The dequoted buffer.
2551 */
2552 
2553 /* ARGSUSED2 */
2554 char *
2555 dequote_map(map, name, av, statp)
2556 	MAP *map;
2557 	char *name;
2558 	char **av;
2559 	int *statp;
2560 {
2561 	register char *p;
2562 	register char *q;
2563 	register char c;
2564 	int anglecnt = 0;
2565 	int cmntcnt = 0;
2566 	int quotecnt = 0;
2567 	int spacecnt = 0;
2568 	bool quotemode = FALSE;
2569 	bool bslashmode = FALSE;
2570 	char spacesub = map->map_spacesub;
2571 
2572 	for (p = q = name; (c = *p++) != '\0'; )
2573 	{
2574 		if (bslashmode)
2575 		{
2576 			bslashmode = FALSE;
2577 			*q++ = c;
2578 			continue;
2579 		}
2580 
2581 		if (c == ' ' && spacesub != '\0')
2582 			c = spacesub;
2583 
2584 		switch (c)
2585 		{
2586 		  case '\\':
2587 			bslashmode = TRUE;
2588 			break;
2589 
2590 		  case '(':
2591 			cmntcnt++;
2592 			break;
2593 
2594 		  case ')':
2595 			if (cmntcnt-- <= 0)
2596 				return NULL;
2597 			break;
2598 
2599 		  case ' ':
2600 		  case '\t':
2601 			spacecnt++;
2602 			break;
2603 		}
2604 
2605 		if (cmntcnt > 0)
2606 		{
2607 			*q++ = c;
2608 			continue;
2609 		}
2610 
2611 		switch (c)
2612 		{
2613 		  case '"':
2614 			quotemode = !quotemode;
2615 			quotecnt++;
2616 			continue;
2617 
2618 		  case '<':
2619 			anglecnt++;
2620 			break;
2621 
2622 		  case '>':
2623 			if (anglecnt-- <= 0)
2624 				return NULL;
2625 			break;
2626 		}
2627 		*q++ = c;
2628 	}
2629 
2630 	if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2631 	    quotemode || quotecnt <= 0 || spacecnt != 0)
2632 		return NULL;
2633 	*q++ = '\0';
2634 	return map_rewrite(map, name, strlen(name), NULL);
2635 }
2636 /*
2637 **  RSCHECK -- check string(s) for validity using rewriting sets
2638 **
2639 **	Parameters:
2640 **		rwset -- the rewriting set to use.
2641 **		p1 -- the first string to check.
2642 **		p2 -- the second string to check -- may be null.
2643 **		e -- the current envelope.
2644 **		rmcomm -- remove comments?
2645 **		cnt -- count rejections (statistics)?
2646 **		logl -- logging level
2647 **		host -- NULL or relay host.
2648 **
2649 **	Returns:
2650 **		EX_OK -- if the rwset doesn't resolve to $#error
2651 **		else -- the failure status (message printed)
2652 */
2653 
2654 int
2655 rscheck(rwset, p1, p2, e, rmcomm, cnt, logl, host)
2656 	char *rwset;
2657 	char *p1;
2658 	char *p2;
2659 	ENVELOPE *e;
2660 	bool rmcomm, cnt;
2661 	int logl;
2662 	char *host;
2663 {
2664 	char *buf;
2665 	int bufsize;
2666 	int saveexitstat;
2667 	int rstat = EX_OK;
2668 	char **pvp;
2669 	int rsno;
2670 	bool discard = FALSE;
2671 	auto ADDRESS a1;
2672 	bool saveQuickAbort = QuickAbort;
2673 	bool saveSuprErrs = SuprErrs;
2674 	char buf0[MAXLINE];
2675 	char pvpbuf[PSBUFSIZE];
2676 	extern char MsgBuf[];
2677 
2678 	if (tTd(48, 2))
2679 		dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
2680 			p2 == NULL ? "(NULL)" : p2);
2681 
2682 	rsno = strtorwset(rwset, NULL, ST_FIND);
2683 	if (rsno < 0)
2684 		return EX_OK;
2685 
2686 	if (p2 != NULL)
2687 	{
2688 		bufsize = strlen(p1) + strlen(p2) + 2;
2689 		if (bufsize > sizeof buf0)
2690 			buf = xalloc(bufsize);
2691 		else
2692 		{
2693 			buf = buf0;
2694 			bufsize = sizeof buf0;
2695 		}
2696 		(void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
2697 	}
2698 	else
2699 	{
2700 		bufsize = strlen(p1) + 1;
2701 		if (bufsize > sizeof buf0)
2702 			buf = xalloc(bufsize);
2703 		else
2704 		{
2705 			buf = buf0;
2706 			bufsize = sizeof buf0;
2707 		}
2708 		(void) snprintf(buf, bufsize, "%s", p1);
2709 	}
2710 	SuprErrs = TRUE;
2711 	QuickAbort = FALSE;
2712 	pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL,
2713 		      rmcomm ? NULL : TokTypeNoC);
2714 	SuprErrs = saveSuprErrs;
2715 	if (pvp == NULL)
2716 	{
2717 		if (tTd(48, 2))
2718 			dprintf("rscheck: cannot prescan input\n");
2719 /*
2720 		syserr("rscheck: cannot prescan input: \"%s\"",
2721 			shortenstring(buf, MAXSHORTSTR));
2722 		rstat = EX_DATAERR;
2723 */
2724 		goto finis;
2725 	}
2726 
2727 	MapOpenErr = FALSE;
2728 	(void) rewrite(pvp, rsno, 0, e);
2729 	if (MapOpenErr)
2730 	{
2731   		usrerrenh("4.3.0", "451 Temporary failure");
2732 		rstat = EX_TEMPFAIL;
2733 		goto finis;
2734 	}
2735 
2736 	if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
2737 	    pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
2738 			       strcmp(pvp[1], "discard") != 0))
2739 	{
2740 		goto finis;
2741 	}
2742 
2743 	if (strcmp(pvp[1], "discard") == 0)
2744 	{
2745 		if (tTd(48, 2))
2746 			dprintf("rscheck: discard mailer selected\n");
2747 		e->e_flags |= EF_DISCARD;
2748 		discard = TRUE;
2749 	}
2750 	else
2751 	{
2752 		int savelogusrerrs = LogUsrErrs;
2753 		static bool logged = FALSE;
2754 
2755 		/* got an error -- process it */
2756 		saveexitstat = ExitStat;
2757 		LogUsrErrs = FALSE;
2758 		(void) buildaddr(pvp, &a1, 0, e);
2759 		LogUsrErrs = savelogusrerrs;
2760 		rstat = ExitStat;
2761 		ExitStat = saveexitstat;
2762 		if (!logged)
2763 		{
2764 			if (cnt)
2765 				markstats(e, &a1, TRUE);
2766 			logged = TRUE;
2767 		}
2768 	}
2769 
2770 	if (LogLevel >= logl)
2771 	{
2772 		char *relay;
2773 		char *p;
2774 		char lbuf[MAXLINE];
2775 
2776 		p = lbuf;
2777 		if (p2 != NULL)
2778 		{
2779 			snprintf(p, SPACELEFT(lbuf, p),
2780 				", arg2=%s",
2781 				p2);
2782 			p += strlen(p);
2783 		}
2784 
2785 		if (host != NULL)
2786 			relay = host;
2787 		else
2788 			relay = macvalue('_', e);
2789 		if (relay != NULL)
2790 		{
2791 			snprintf(p, SPACELEFT(lbuf, p),
2792 				", relay=%s", relay);
2793 			p += strlen(p);
2794 		}
2795 		*p = '\0';
2796 		if (discard)
2797 			sm_syslog(LOG_NOTICE, e->e_id,
2798 				  "ruleset=%s, arg1=%s%s, discard",
2799 				  rwset, p1, lbuf);
2800 		else
2801 			sm_syslog(LOG_NOTICE, e->e_id,
2802 				  "ruleset=%s, arg1=%s%s, reject=%s",
2803 				  rwset, p1, lbuf, MsgBuf);
2804 	}
2805 
2806  finis:
2807 	/* clean up */
2808 	QuickAbort = saveQuickAbort;
2809 	setstat(rstat);
2810 	if (buf != buf0)
2811 		sm_free(buf);
2812 
2813 	if (rstat != EX_OK && QuickAbort)
2814 		longjmp(TopFrame, 2);
2815 	return rstat;
2816 }
2817