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