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