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