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