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