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