source: trunk/third/sendmail/src/parseaddr.c @ 12554

Revision 12554, 55.9 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)parseaddr.c 8.156 (Berkeley) 10/27/1998";
15#endif /* not lint */
16
17# include "sendmail.h"
18
19/*
20**  PARSEADDR -- Parse an address
21**
22**      Parses an address and breaks it up into three parts: a
23**      net to transmit the message on, the host to transmit it
24**      to, and a user on that host.  These are loaded into an
25**      ADDRESS header with the values squirreled away if necessary.
26**      The "user" part may not be a real user; the process may
27**      just reoccur on that machine.  For example, on a machine
28**      with an arpanet connection, the address
29**              csvax.bill@berkeley
30**      will break up to a "user" of 'csvax.bill' and a host
31**      of 'berkeley' -- to be transmitted over the arpanet.
32**
33**      Parameters:
34**              addr -- the address to parse.
35**              a -- a pointer to the address descriptor buffer.
36**                      If NULL, a header will be created.
37**              flags -- describe detail for parsing.  See RF_ definitions
38**                      in sendmail.h.
39**              delim -- the character to terminate the address, passed
40**                      to prescan.
41**              delimptr -- if non-NULL, set to the location of the
42**                      delim character that was found.
43**              e -- the envelope that will contain this address.
44**
45**      Returns:
46**              A pointer to the address descriptor header (`a' if
47**                      `a' is non-NULL).
48**              NULL on error.
49**
50**      Side Effects:
51**              none
52*/
53
54/* following delimiters are inherent to the internal algorithms */
55# define DELIMCHARS     "()<>,;\r\n"    /* default word delimiters */
56
57ADDRESS *
58parseaddr(addr, a, flags, delim, delimptr, e)
59        char *addr;
60        register ADDRESS *a;
61        int flags;
62        int delim;
63        char **delimptr;
64        register ENVELOPE *e;
65{
66        register char **pvp;
67        auto char *delimptrbuf;
68        bool queueup;
69        char pvpbuf[PSBUFSIZE];
70        extern bool invalidaddr __P((char *, char *));
71        extern void allocaddr __P((ADDRESS *, int, char *));
72
73        /*
74        **  Initialize and prescan address.
75        */
76
77        e->e_to = addr;
78        if (tTd(20, 1))
79                printf("\n--parseaddr(%s)\n", addr);
80
81        if (delimptr == NULL)
82                delimptr = &delimptrbuf;
83
84        pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL);
85        if (pvp == NULL)
86        {
87                if (tTd(20, 1))
88                        printf("parseaddr-->NULL\n");
89                return (NULL);
90        }
91
92        if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr))
93        {
94                if (tTd(20, 1))
95                        printf("parseaddr-->bad address\n");
96                return NULL;
97        }
98
99        /*
100        **  Save addr if we are going to have to.
101        **
102        **      We have to do this early because there is a chance that
103        **      the map lookups in the rewriting rules could clobber
104        **      static memory somewhere.
105        */
106
107        if (bitset(RF_COPYPADDR, flags) && addr != NULL)
108        {
109                char savec = **delimptr;
110
111                if (savec != '\0')
112                        **delimptr = '\0';
113                e->e_to = addr = newstr(addr);
114                if (savec != '\0')
115                        **delimptr = savec;
116        }
117
118        /*
119        **  Apply rewriting rules.
120        **      Ruleset 0 does basic parsing.  It must resolve.
121        */
122
123        queueup = FALSE;
124        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
125                queueup = TRUE;
126        if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL)
127                queueup = TRUE;
128
129
130        /*
131        **  Build canonical address from pvp.
132        */
133
134        a = buildaddr(pvp, a, flags, e);
135
136        /*
137        **  Make local copies of the host & user and then
138        **  transport them out.
139        */
140
141        allocaddr(a, flags, addr);
142        if (bitset(QBADADDR, a->q_flags))
143                return a;
144
145        /*
146        **  If there was a parsing failure, mark it for queueing.
147        */
148
149        if (queueup && OpMode != MD_INITALIAS)
150        {
151                char *msg = "Transient parse error -- message queued for future delivery";
152
153                if (e->e_sendmode == SM_DEFER)
154                        msg = "Deferring message until queue run";
155                if (tTd(20, 1))
156                        printf("parseaddr: queuing message\n");
157                message(msg);
158                if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
159                        e->e_message = newstr(msg);
160                a->q_flags |= QQUEUEUP;
161                a->q_status = "4.4.3";
162        }
163
164        /*
165        **  Compute return value.
166        */
167
168        if (tTd(20, 1))
169        {
170                printf("parseaddr-->");
171                printaddr(a, FALSE);
172        }
173
174        return (a);
175}
176/*
177**  INVALIDADDR -- check for address containing meta-characters
178**
179**      Parameters:
180**              addr -- the address to check.
181**
182**      Returns:
183**              TRUE -- if the address has any "wierd" characters
184**              FALSE -- otherwise.
185*/
186
187bool
188invalidaddr(addr, delimptr)
189        register char *addr;
190        char *delimptr;
191{
192        char savedelim = '\0';
193
194        if (delimptr != NULL)
195        {
196                savedelim = *delimptr;
197                if (savedelim != '\0')
198                        *delimptr = '\0';
199        }
200        if (strlen(addr) > TOBUFSIZE - 2)
201        {
202                usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2);
203                goto failure;
204        }
205        for (; *addr != '\0'; addr++)
206        {
207                if ((*addr & 0340) == 0200)
208                        break;
209        }
210        if (*addr == '\0')
211        {
212                if (delimptr != NULL && savedelim != '\0')
213                        *delimptr = savedelim;
214                return FALSE;
215        }
216        setstat(EX_USAGE);
217        usrerr("553 Address contained invalid control characters");
218failure:
219        if (delimptr != NULL && savedelim != '\0')
220                *delimptr = savedelim;
221        return TRUE;
222}
223/*
224**  ALLOCADDR -- do local allocations of address on demand.
225**
226**      Also lowercases the host name if requested.
227**
228**      Parameters:
229**              a -- the address to reallocate.
230**              flags -- the copy flag (see RF_ definitions in sendmail.h
231**                      for a description).
232**              paddr -- the printname of the address.
233**
234**      Returns:
235**              none.
236**
237**      Side Effects:
238**              Copies portions of a into local buffers as requested.
239*/
240
241void
242allocaddr(a, flags, paddr)
243        register ADDRESS *a;
244        int flags;
245        char *paddr;
246{
247        if (tTd(24, 4))
248                printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
249
250        a->q_paddr = paddr;
251
252        if (a->q_user == NULL)
253                a->q_user = "";
254        if (a->q_host == NULL)
255                a->q_host = "";
256
257        if (bitset(RF_COPYPARSE, flags))
258        {
259                a->q_host = newstr(a->q_host);
260                if (a->q_user != a->q_paddr)
261                        a->q_user = newstr(a->q_user);
262        }
263
264        if (a->q_paddr == NULL)
265                a->q_paddr = a->q_user;
266}
267/*
268**  PRESCAN -- Prescan name and make it canonical
269**
270**      Scans a name and turns it into a set of tokens.  This process
271**      deletes blanks and comments (in parentheses).
272**
273**      This routine knows about quoted strings and angle brackets.
274**
275**      There are certain subtleties to this routine.  The one that
276**      comes to mind now is that backslashes on the ends of names
277**      are silently stripped off; this is intentional.  The problem
278**      is that some versions of sndmsg (like at LBL) set the kill
279**      character to something other than @ when reading addresses;
280**      so people type "csvax.eric\@berkeley" -- which screws up the
281**      berknet mailer.
282**
283**      Parameters:
284**              addr -- the name to chomp.
285**              delim -- the delimiter for the address, normally
286**                      '\0' or ','; \0 is accepted in any case.
287**                      If '\t' then we are reading the .cf file.
288**              pvpbuf -- place to put the saved text -- note that
289**                      the pointers are static.
290**              pvpbsize -- size of pvpbuf.
291**              delimptr -- if non-NULL, set to the location of the
292**                      terminating delimiter.
293**              toktab -- if set, a token table to use for parsing.
294**                      If NULL, use the default table.
295**
296**      Returns:
297**              A pointer to a vector of tokens.
298**              NULL on error.
299*/
300
301/* states and character types */
302# define OPR            0       /* operator */
303# define ATM            1       /* atom */
304# define QST            2       /* in quoted string */
305# define SPC            3       /* chewing up spaces */
306# define ONE            4       /* pick up one character */
307# define ILL            5       /* illegal character */
308
309# define NSTATES        6       /* number of states */
310# define TYPE           017     /* mask to select state type */
311
312/* meta bits for table */
313# define M              020     /* meta character; don't pass through */
314# define B              040     /* cause a break */
315# define MB             M|B     /* meta-break */
316
317static short StateTab[NSTATES][NSTATES] =
318{
319   /*   oldst   chtype> OPR     ATM     QST     SPC     ONE     ILL     */
320        /*OPR*/ {       OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|MB  },
321        /*ATM*/ {       OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,  ILL|MB  },
322        /*QST*/ {       QST,    QST,    OPR,    QST,    QST,    QST     },
323        /*SPC*/ {       OPR,    ATM,    QST,    SPC|M,  ONE,    ILL|MB  },
324        /*ONE*/ {       OPR,    OPR,    OPR,    OPR,    OPR,    ILL|MB  },
325        /*ILL*/ {       OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|M   },
326};
327
328/* token type table -- it gets modified with $o characters */
329static u_char   TokTypeTab[256] =
330{
331    /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
332        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
333    /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
334        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
335    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
336        SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
337    /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
338        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
339    /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
340        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
341    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
342        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
343    /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
344        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
345    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
346        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
347
348    /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
349        OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
350    /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
351        OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
352    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
353        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
354    /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
355        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
356    /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
357        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
358    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
359        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
360    /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
361        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
362    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
363        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
364};
365
366/* token type table for MIME parsing */
367u_char  MimeTokenTab[256] =
368{
369    /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
370        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
371    /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
372        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
373    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
374        SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
375    /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
376        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
377    /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
378        OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
379    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
380        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
381    /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
382        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
383    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
384        ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
385
386    /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
387        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
388    /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
389        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
390    /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
391        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
392    /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
393        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
394    /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
395        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
396    /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
397        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
398    /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
399        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
400    /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
401        ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
402};
403
404
405# define NOCHAR         -1      /* signal nothing in lookahead token */
406
407char **
408prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab)
409        char *addr;
410        int delim;
411        char pvpbuf[];
412        int pvpbsize;
413        char **delimptr;
414        u_char *toktab;
415{
416        register char *p;
417        register char *q;
418        register int c;
419        char **avp;
420        bool bslashmode;
421        bool route_syntax;
422        int cmntcnt;
423        int anglecnt;
424        char *tok;
425        int state;
426        int newstate;
427        char *saveto = CurEnv->e_to;
428        static char *av[MAXATOM+1];
429        static char firsttime = TRUE;
430        extern int errno;
431
432        if (firsttime)
433        {
434                /* initialize the token type table */
435                char obuf[50];
436
437                firsttime = FALSE;
438                if (OperatorChars == NULL)
439                {
440                        if (ConfigLevel < 7)
441                                OperatorChars = macvalue('o', CurEnv);
442                        if (OperatorChars == NULL)
443                                OperatorChars = ".:@[]";
444                }
445                expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv);
446                strcat(obuf, DELIMCHARS);
447                for (p = obuf; *p != '\0'; p++)
448                {
449                        if (TokTypeTab[*p & 0xff] == ATM)
450                                TokTypeTab[*p & 0xff] = OPR;
451                }
452        }
453        if (toktab == NULL)
454                toktab = TokTypeTab;
455
456        /* make sure error messages don't have garbage on them */
457        errno = 0;
458
459        q = pvpbuf;
460        bslashmode = FALSE;
461        route_syntax = FALSE;
462        cmntcnt = 0;
463        anglecnt = 0;
464        avp = av;
465        state = ATM;
466        c = NOCHAR;
467        p = addr;
468        CurEnv->e_to = p;
469        if (tTd(22, 11))
470        {
471                printf("prescan: ");
472                xputs(p);
473                (void) putchar('\n');
474        }
475
476        do
477        {
478                /* read a token */
479                tok = q;
480                for (;;)
481                {
482                        /* store away any old lookahead character */
483                        if (c != NOCHAR && !bslashmode)
484                        {
485                                /* see if there is room */
486                                if (q >= &pvpbuf[pvpbsize - 5])
487                                {
488                                        usrerr("553 Address too long");
489                                        if (strlen(addr) > (SIZE_T) MAXNAME)
490                                                addr[MAXNAME] = '\0';
491        returnnull:
492                                        if (delimptr != NULL)
493                                                *delimptr = p;
494                                        CurEnv->e_to = saveto;
495                                        return (NULL);
496                                }
497
498                                /* squirrel it away */
499                                *q++ = c;
500                        }
501
502                        /* read a new input character */
503                        c = *p++;
504                        if (c == '\0')
505                        {
506                                /* diagnose and patch up bad syntax */
507                                if (state == QST)
508                                {
509                                        usrerr("653 Unbalanced '\"'");
510                                        c = '"';
511                                }
512                                else if (cmntcnt > 0)
513                                {
514                                        usrerr("653 Unbalanced '('");
515                                        c = ')';
516                                }
517                                else if (anglecnt > 0)
518                                {
519                                        c = '>';
520                                        usrerr("653 Unbalanced '<'");
521                                }
522                                else
523                                        break;
524
525                                p--;
526                        }
527                        else if (c == delim && cmntcnt <= 0 && state != QST)
528                        {
529                                if (anglecnt <= 0)
530                                        break;
531
532                                /* special case for better error management */
533                                if (delim == ',' && !route_syntax)
534                                {
535                                        usrerr("653 Unbalanced '<'");
536                                        c = '>';
537                                        p--;
538                                }
539                        }
540
541                        if (tTd(22, 101))
542                                printf("c=%c, s=%d; ", c, state);
543
544                        /* chew up special characters */
545                        *q = '\0';
546                        if (bslashmode)
547                        {
548                                bslashmode = FALSE;
549
550                                /* kludge \! for naive users */
551                                if (cmntcnt > 0)
552                                {
553                                        c = NOCHAR;
554                                        continue;
555                                }
556                                else if (c != '!' || state == QST)
557                                {
558                                        *q++ = '\\';
559                                        continue;
560                                }
561                        }
562
563                        if (c == '\\')
564                        {
565                                bslashmode = TRUE;
566                        }
567                        else if (state == QST)
568                        {
569                                /* do nothing, just avoid next clauses */
570                        }
571                        else if (c == '(')
572                        {
573                                cmntcnt++;
574                                c = NOCHAR;
575                        }
576                        else if (c == ')')
577                        {
578                                if (cmntcnt <= 0)
579                                {
580                                        usrerr("653 Unbalanced ')'");
581                                        c = NOCHAR;
582                                }
583                                else
584                                        cmntcnt--;
585                        }
586                        else if (cmntcnt > 0)
587                                c = NOCHAR;
588                        else if (c == '<')
589                        {
590                                char *q = p;
591
592                                anglecnt++;
593                                while (isascii(*q) && isspace(*q))
594                                        q++;
595                                if (*q == '@')
596                                        route_syntax = TRUE;
597                        }
598                        else if (c == '>')
599                        {
600                                if (anglecnt <= 0)
601                                {
602                                        usrerr("653 Unbalanced '>'");
603                                        c = NOCHAR;
604                                }
605                                else
606                                        anglecnt--;
607                                route_syntax = FALSE;
608                        }
609                        else if (delim == ' ' && isascii(c) && isspace(c))
610                                c = ' ';
611
612                        if (c == NOCHAR)
613                                continue;
614
615                        /* see if this is end of input */
616                        if (c == delim && anglecnt <= 0 && state != QST)
617                                break;
618
619                        newstate = StateTab[state][toktab[c & 0xff]];
620                        if (tTd(22, 101))
621                                printf("ns=%02o\n", newstate);
622                        state = newstate & TYPE;
623                        if (state == ILL)
624                        {
625                                if (isascii(c) && isprint(c))
626                                        usrerr("653 Illegal character %c", c);
627                                else
628                                        usrerr("653 Illegal character 0x%02x", c);
629                        }
630                        if (bitset(M, newstate))
631                                c = NOCHAR;
632                        if (bitset(B, newstate))
633                                break;
634                }
635
636                /* new token */
637                if (tok != q)
638                {
639                        *q++ = '\0';
640                        if (tTd(22, 36))
641                        {
642                                printf("tok=");
643                                xputs(tok);
644                                (void) putchar('\n');
645                        }
646                        if (avp >= &av[MAXATOM])
647                        {
648                                usrerr("553 prescan: too many tokens");
649                                goto returnnull;
650                        }
651                        if (q - tok > MAXNAME)
652                        {
653                                usrerr("553 prescan: token too long");
654                                goto returnnull;
655                        }
656                        *avp++ = tok;
657                }
658        } while (c != '\0' && (c != delim || anglecnt > 0));
659        *avp = NULL;
660        p--;
661        if (delimptr != NULL)
662                *delimptr = p;
663        if (tTd(22, 12))
664        {
665                printf("prescan==>");
666                printav(av);
667        }
668        CurEnv->e_to = saveto;
669        if (av[0] == NULL)
670        {
671                if (tTd(22, 1))
672                        printf("prescan: null leading token\n");
673                return (NULL);
674        }
675        return (av);
676}
677/*
678**  REWRITE -- apply rewrite rules to token vector.
679**
680**      This routine is an ordered production system.  Each rewrite
681**      rule has a LHS (called the pattern) and a RHS (called the
682**      rewrite); 'rwr' points the the current rewrite rule.
683**
684**      For each rewrite rule, 'avp' points the address vector we
685**      are trying to match against, and 'pvp' points to the pattern.
686**      If pvp points to a special match value (MATCHZANY, MATCHANY,
687**      MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
688**      matched is saved away in the match vector (pointed to by 'mvp').
689**
690**      When a match between avp & pvp does not match, we try to
691**      back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
692**      we must also back out the match in mvp.  If we reach a
693**      MATCHANY or MATCHZANY we just extend the match and start
694**      over again.
695**
696**      When we finally match, we rewrite the address vector
697**      and try over again.
698**
699**      Parameters:
700**              pvp -- pointer to token vector.
701**              ruleset -- the ruleset to use for rewriting.
702**              reclevel -- recursion level (to catch loops).
703**              e -- the current envelope.
704**
705**      Returns:
706**              A status code.  If EX_TEMPFAIL, higher level code should
707**                      attempt recovery.
708**
709**      Side Effects:
710**              pvp is modified.
711*/
712
713struct match
714{
715        char    **first;        /* first token matched */
716        char    **last;         /* last token matched */
717        char    **pattern;      /* pointer to pattern */
718};
719
720# define MAXMATCH       9       /* max params per rewrite */
721
722
723int
724rewrite(pvp, ruleset, reclevel, e)
725        char **pvp;
726        int ruleset;
727        int reclevel;
728        register ENVELOPE *e;
729{
730        register char *ap;              /* address pointer */
731        register char *rp;              /* rewrite pointer */
732        register char **avp;            /* address vector pointer */
733        register char **rvp;            /* rewrite vector pointer */
734        register struct match *mlp;     /* cur ptr into mlist */
735        register struct rewrite *rwr;   /* pointer to current rewrite rule */
736        int ruleno;                     /* current rule number */
737        int rstat = EX_OK;              /* return status */
738        int loopcount;
739        struct match mlist[MAXMATCH];   /* stores match on LHS */
740        char *npvp[MAXATOM+1];          /* temporary space for rebuild */
741        char buf[MAXLINE];
742        extern int callsubr __P((char**, int, ENVELOPE *));
743        extern int sm_strcasecmp __P((char *, char *));
744
745        if (OpMode == MD_TEST || tTd(21, 1))
746        {
747                printf("rewrite: ruleset %3d   input:", ruleset);
748                printav(pvp);
749        }
750        if (ruleset < 0 || ruleset >= MAXRWSETS)
751        {
752                syserr("554 rewrite: illegal ruleset number %d", ruleset);
753                return EX_CONFIG;
754        }
755        if (reclevel++ > MaxRuleRecursion)
756        {
757                syserr("rewrite: excessive recursion (max %d), ruleset %d",
758                        MaxRuleRecursion, ruleset);
759                return EX_CONFIG;
760        }
761        if (pvp == NULL)
762                return EX_USAGE;
763
764        /*
765        **  Run through the list of rewrite rules, applying
766        **      any that match.
767        */
768
769        ruleno = 1;
770        loopcount = 0;
771        for (rwr = RewriteRules[ruleset]; rwr != NULL; )
772        {
773                int stat;
774
775                /* if already canonical, quit now */
776                if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
777                        break;
778
779                if (tTd(21, 12))
780                {
781                        printf("-----trying rule:");
782                        printav(rwr->r_lhs);
783                }
784
785                /* try to match on this rule */
786                mlp = mlist;
787                rvp = rwr->r_lhs;
788                avp = pvp;
789                if (++loopcount > 100)
790                {
791                        syserr("554 Infinite loop in ruleset %d, rule %d",
792                                ruleset, ruleno);
793                        if (tTd(21, 1))
794                        {
795                                printf("workspace: ");
796                                printav(pvp);
797                        }
798                        break;
799                }
800
801                while ((ap = *avp) != NULL || *rvp != NULL)
802                {
803                        rp = *rvp;
804                        if (tTd(21, 35))
805                        {
806                                printf("ADVANCE rp=");
807                                xputs(rp);
808                                printf(", ap=");
809                                xputs(ap);
810                                printf("\n");
811                        }
812                        if (rp == NULL)
813                        {
814                                /* end-of-pattern before end-of-address */
815                                goto backup;
816                        }
817                        if (ap == NULL && (*rp & 0377) != MATCHZANY &&
818                            (*rp & 0377) != MATCHZERO)
819                        {
820                                /* end-of-input with patterns left */
821                                goto backup;
822                        }
823
824                        switch (*rp & 0377)
825                        {
826                          case MATCHCLASS:
827                                /* match any phrase in a class */
828                                mlp->pattern = rvp;
829                                mlp->first = avp;
830        extendclass:
831                                ap = *avp;
832                                if (ap == NULL)
833                                        goto backup;
834                                mlp->last = avp++;
835                                cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0');
836                                if (!wordinclass(buf, rp[1]))
837                                {
838                                        if (tTd(21, 36))
839                                        {
840                                                printf("EXTEND  rp=");
841                                                xputs(rp);
842                                                printf(", ap=");
843                                                xputs(ap);
844                                                printf("\n");
845                                        }
846                                        goto extendclass;
847                                }
848                                if (tTd(21, 36))
849                                        printf("CLMATCH\n");
850                                mlp++;
851                                break;
852
853                          case MATCHNCLASS:
854                                /* match any token not in a class */
855                                if (wordinclass(ap, rp[1]))
856                                        goto backup;
857
858                                /* fall through */
859
860                          case MATCHONE:
861                          case MATCHANY:
862                                /* match exactly one token */
863                                mlp->pattern = rvp;
864                                mlp->first = avp;
865                                mlp->last = avp++;
866                                mlp++;
867                                break;
868
869                          case MATCHZANY:
870                                /* match zero or more tokens */
871                                mlp->pattern = rvp;
872                                mlp->first = avp;
873                                mlp->last = avp - 1;
874                                mlp++;
875                                break;
876
877                          case MATCHZERO:
878                                /* match zero tokens */
879                                break;
880
881                          case MACRODEXPAND:
882                                /*
883                                **  Match against run-time macro.
884                                **  This algorithm is broken for the
885                                **  general case (no recursive macros,
886                                **  improper tokenization) but should
887                                **  work for the usual cases.
888                                */
889
890                                ap = macvalue(rp[1], e);
891                                mlp->first = avp;
892                                if (tTd(21, 2))
893                                        printf("rewrite: LHS $&%s => \"%s\"\n",
894                                                macname(rp[1]),
895                                                ap == NULL ? "(NULL)" : ap);
896
897                                if (ap == NULL)
898                                        break;
899                                while (*ap != '\0')
900                                {
901                                        if (*avp == NULL ||
902                                            strncasecmp(ap, *avp, strlen(*avp)) != 0)
903                                        {
904                                                /* no match */
905                                                avp = mlp->first;
906                                                goto backup;
907                                        }
908                                        ap += strlen(*avp++);
909                                }
910
911                                /* match */
912                                break;
913
914                          default:
915                                /* must have exact match */
916                                if (sm_strcasecmp(rp, ap))
917                                        goto backup;
918                                avp++;
919                                break;
920                        }
921
922                        /* successful match on this token */
923                        rvp++;
924                        continue;
925
926          backup:
927                        /* match failed -- back up */
928                        while (--mlp >= mlist)
929                        {
930                                rvp = mlp->pattern;
931                                rp = *rvp;
932                                avp = mlp->last + 1;
933                                ap = *avp;
934
935                                if (tTd(21, 36))
936                                {
937                                        printf("BACKUP  rp=");
938                                        xputs(rp);
939                                        printf(", ap=");
940                                        xputs(ap);
941                                        printf("\n");
942                                }
943
944                                if (ap == NULL)
945                                {
946                                        /* run off the end -- back up again */
947                                        continue;
948                                }
949                                if ((*rp & 0377) == MATCHANY ||
950                                    (*rp & 0377) == MATCHZANY)
951                                {
952                                        /* extend binding and continue */
953                                        mlp->last = avp++;
954                                        rvp++;
955                                        mlp++;
956                                        break;
957                                }
958                                if ((*rp & 0377) == MATCHCLASS)
959                                {
960                                        /* extend binding and try again */
961                                        mlp->last = avp;
962                                        goto extendclass;
963                                }
964                        }
965
966                        if (mlp < mlist)
967                        {
968                                /* total failure to match */
969                                break;
970                        }
971                }
972
973                /*
974                **  See if we successfully matched
975                */
976
977                if (mlp < mlist || *rvp != NULL)
978                {
979                        if (tTd(21, 10))
980                                printf("----- rule fails\n");
981                        rwr = rwr->r_next;
982                        ruleno++;
983                        loopcount = 0;
984                        continue;
985                }
986
987                rvp = rwr->r_rhs;
988                if (tTd(21, 12))
989                {
990                        printf("-----rule matches:");
991                        printav(rvp);
992                }
993
994                rp = *rvp;
995                if ((*rp & 0377) == CANONUSER)
996                {
997                        rvp++;
998                        rwr = rwr->r_next;
999                        ruleno++;
1000                        loopcount = 0;
1001                }
1002                else if ((*rp & 0377) == CANONHOST)
1003                {
1004                        rvp++;
1005                        rwr = NULL;
1006                }
1007
1008                /* substitute */
1009                for (avp = npvp; *rvp != NULL; rvp++)
1010                {
1011                        register struct match *m;
1012                        register char **pp;
1013
1014                        rp = *rvp;
1015                        if ((*rp & 0377) == MATCHREPL)
1016                        {
1017                                /* substitute from LHS */
1018                                m = &mlist[rp[1] - '1'];
1019                                if (m < mlist || m >= mlp)
1020                                {
1021                                        syserr("554 rewrite: ruleset %d: replacement $%c out of bounds",
1022                                                ruleset, rp[1]);
1023                                        return EX_CONFIG;
1024                                }
1025                                if (tTd(21, 15))
1026                                {
1027                                        printf("$%c:", rp[1]);
1028                                        pp = m->first;
1029                                        while (pp <= m->last)
1030                                        {
1031                                                printf(" %lx=\"", (u_long) *pp);
1032                                                (void) fflush(stdout);
1033                                                printf("%s\"", *pp++);
1034                                        }
1035                                        printf("\n");
1036                                }
1037                                pp = m->first;
1038                                while (pp <= m->last)
1039                                {
1040                                        if (avp >= &npvp[MAXATOM])
1041                                        {
1042                                                syserr("554 rewrite: expansion too long");
1043                                                return EX_DATAERR;
1044                                        }
1045                                        *avp++ = *pp++;
1046                                }
1047                        }
1048                        else
1049                        {
1050                                /* some sort of replacement */
1051                                if (avp >= &npvp[MAXATOM])
1052                                {
1053        toolong:
1054                                        syserr("554 rewrite: expansion too long");
1055                                        return EX_DATAERR;
1056                                }
1057                                if ((*rp & 0377) != MACRODEXPAND)
1058                                {
1059                                        /* vanilla replacement */
1060                                        *avp++ = rp;
1061                                }
1062                                else
1063                                {
1064                                        /* $&x replacement */
1065                                        char *mval = macvalue(rp[1], e);
1066                                        char **xpvp;
1067                                        int trsize = 0;
1068                                        static size_t pvpb1_size = 0;
1069                                        static char **pvpb1 = NULL;
1070                                        char pvpbuf[PSBUFSIZE];
1071
1072                                        if (tTd(21, 2))
1073                                                printf("rewrite: RHS $&%s => \"%s\"\n",
1074                                                        macname(rp[1]),
1075                                                        mval == NULL ? "(NULL)" : mval);
1076                                        if (mval == NULL || *mval == '\0')
1077                                                continue;
1078
1079                                        /* save the remainder of the input */
1080                                        for (xpvp = pvp; *xpvp != NULL; xpvp++)
1081                                                trsize += sizeof *xpvp;
1082                                        if (trsize > pvpb1_size)
1083                                        {
1084                                                if (pvpb1 != NULL)
1085                                                        free(pvpb1);
1086                                                pvpb1 = (char **)xalloc(trsize);
1087                                                pvpb1_size = trsize;
1088                                        }
1089
1090                                        bcopy((char *) pvp, (char *) pvpb1, trsize);
1091
1092                                        /* scan the new replacement */
1093                                        xpvp = prescan(mval, '\0', pvpbuf,
1094                                                       sizeof pvpbuf, NULL, NULL);
1095                                        if (xpvp == NULL)
1096                                        {
1097                                                /* prescan pre-printed error */
1098                                                return EX_DATAERR;
1099                                        }
1100
1101                                        /* insert it into the output stream */
1102                                        while (*xpvp != NULL)
1103                                        {
1104                                                if (tTd(21, 19))
1105                                                        printf(" ... %s\n", *xpvp);
1106                                                *avp++ = newstr(*xpvp);
1107                                                if (avp >= &npvp[MAXATOM])
1108                                                        goto toolong;
1109                                                xpvp++;
1110                                        }
1111                                        if (tTd(21, 19))
1112                                                printf(" ... DONE\n");
1113
1114                                        /* restore the old trailing input */
1115                                        bcopy((char *) pvpb1, (char *) pvp, trsize);
1116                                }
1117                        }
1118                }
1119                *avp++ = NULL;
1120
1121                /*
1122                **  Check for any hostname/keyword lookups.
1123                */
1124
1125                for (rvp = npvp; *rvp != NULL; rvp++)
1126                {
1127                        char **hbrvp;
1128                        char **xpvp;
1129                        int trsize;
1130                        char *replac;
1131                        int endtoken;
1132                        STAB *map;
1133                        char *mapname;
1134                        char **key_rvp;
1135                        char **arg_rvp;
1136                        char **default_rvp;
1137                        char buf[MAXNAME + 1];
1138                        char *pvpb1[MAXATOM + 1];
1139                        char *argvect[10];
1140                        char pvpbuf[PSBUFSIZE];
1141                        char *nullpvp[1];
1142                        extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
1143
1144                        if ((**rvp & 0377) != HOSTBEGIN &&
1145                            (**rvp & 0377) != LOOKUPBEGIN)
1146                                continue;
1147
1148                        /*
1149                        **  Got a hostname/keyword lookup.
1150                        **
1151                        **      This could be optimized fairly easily.
1152                        */
1153
1154                        hbrvp = rvp;
1155                        if ((**rvp & 0377) == HOSTBEGIN)
1156                        {
1157                                endtoken = HOSTEND;
1158                                mapname = "host";
1159                        }
1160                        else
1161                        {
1162                                endtoken = LOOKUPEND;
1163                                mapname = *++rvp;
1164                        }
1165                        map = stab(mapname, ST_MAP, ST_FIND);
1166                        if (map == NULL)
1167                                syserr("554 rewrite: map %s not found", mapname);
1168
1169                        /* extract the match part */
1170                        key_rvp = ++rvp;
1171                        default_rvp = NULL;
1172                        arg_rvp = argvect;
1173                        xpvp = NULL;
1174                        replac = pvpbuf;
1175                        while (*rvp != NULL && (**rvp & 0377) != endtoken)
1176                        {
1177                                int nodetype = **rvp & 0377;
1178
1179                                if (nodetype != CANONHOST && nodetype != CANONUSER)
1180                                {
1181                                        rvp++;
1182                                        continue;
1183                                }
1184
1185                                *rvp++ = NULL;
1186
1187                                if (xpvp != NULL)
1188                                {
1189                                        cataddr(xpvp, NULL, replac,
1190                                                &pvpbuf[sizeof pvpbuf] - replac,
1191                                                '\0');
1192                                        *++arg_rvp = replac;
1193                                        replac += strlen(replac) + 1;
1194                                        xpvp = NULL;
1195                                }
1196                                switch (nodetype)
1197                                {
1198                                  case CANONHOST:
1199                                        xpvp = rvp;
1200                                        break;
1201
1202                                  case CANONUSER:
1203                                        default_rvp = rvp;
1204                                        break;
1205                                }
1206                        }
1207                        if (*rvp != NULL)
1208                                *rvp++ = NULL;
1209                        if (xpvp != NULL)
1210                        {
1211                                cataddr(xpvp, NULL, replac,
1212                                        &pvpbuf[sizeof pvpbuf] - replac,
1213                                        '\0');
1214                                *++arg_rvp = replac;
1215                        }
1216                        *++arg_rvp = NULL;
1217
1218                        /* save the remainder of the input string */
1219                        trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1220                        bcopy((char *) rvp, (char *) pvpb1, trsize);
1221
1222                        /* look it up */
1223                        cataddr(key_rvp, NULL, buf, sizeof buf, '\0');
1224                        argvect[0] = buf;
1225                        replac = map_lookup(map, buf, argvect, &rstat, e);
1226
1227                        /* if no replacement, use default */
1228                        if (replac == NULL && default_rvp != NULL)
1229                        {
1230                                /* create the default */
1231                                cataddr(default_rvp, NULL, buf, sizeof buf, '\0');
1232                                replac = buf;
1233                        }
1234
1235                        if (replac == NULL)
1236                        {
1237                                xpvp = key_rvp;
1238                        }
1239                        else if (*replac == '\0')
1240                        {
1241                                /* null replacement */
1242                                nullpvp[0] = NULL;
1243                                xpvp = nullpvp;
1244                        }
1245                        else
1246                        {
1247                                /* scan the new replacement */
1248                                xpvp = prescan(replac, '\0', pvpbuf,
1249                                               sizeof pvpbuf, NULL, NULL);
1250                                if (xpvp == NULL)
1251                                {
1252                                        /* prescan already printed error */
1253                                        return EX_DATAERR;
1254                                }
1255                        }
1256
1257                        /* append it to the token list */
1258                        for (avp = hbrvp; *xpvp != NULL; xpvp++)
1259                        {
1260                                *avp++ = newstr(*xpvp);
1261                                if (avp >= &npvp[MAXATOM])
1262                                        goto toolong;
1263                        }
1264
1265                        /* restore the old trailing information */
1266                        rvp = avp - 1;
1267                        for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1268                                if (avp >= &npvp[MAXATOM])
1269                                        goto toolong;
1270                }
1271
1272                /*
1273                **  Check for subroutine calls.
1274                */
1275
1276                stat = callsubr(npvp, reclevel, e);
1277                if (rstat == EX_OK || stat == EX_TEMPFAIL)
1278                        rstat = stat;
1279
1280                /* copy vector back into original space. */
1281                for (avp = npvp; *avp++ != NULL;)
1282                        continue;
1283                bcopy((char *) npvp, (char *) pvp,
1284                      (int) (avp - npvp) * sizeof *avp);
1285               
1286                if (tTd(21, 4))
1287                {
1288                        printf("rewritten as:");
1289                        printav(pvp);
1290                }
1291        }
1292
1293        if (OpMode == MD_TEST || tTd(21, 1))
1294        {
1295                printf("rewrite: ruleset %3d returns:", ruleset);
1296                printav(pvp);
1297        }
1298
1299        return rstat;
1300}
1301/*
1302**  CALLSUBR -- call subroutines in rewrite vector
1303**
1304**      Parameters:
1305**              pvp -- pointer to token vector.
1306**              reclevel -- the current recursion level.
1307**              e -- the current envelope.
1308**
1309**      Returns:
1310**              The status from the subroutine call.
1311**
1312**      Side Effects:
1313**              pvp is modified.
1314*/
1315
1316int
1317callsubr(pvp, reclevel, e)
1318        char **pvp;
1319        int reclevel;
1320        ENVELOPE *e;
1321{
1322        char **avp;
1323        char **rvp;
1324        register int i;
1325        int subr;
1326        int stat;
1327        int rstat = EX_OK;
1328        char *tpvp[MAXATOM + 1];
1329
1330        for (avp = pvp; *avp != NULL; avp++)
1331        {
1332                if ((**avp & 0377) == CALLSUBR && avp[1] != NULL)
1333                {
1334                        stripquotes(avp[1]);
1335                        subr = strtorwset(avp[1], NULL, ST_FIND);
1336                        if (subr < 0)
1337                        {
1338                                syserr("Unknown ruleset %s", avp[1]);
1339                                return EX_CONFIG;
1340                        }
1341
1342                        if (tTd(21, 3))
1343                                printf("-----callsubr %s (%d)\n", avp[1], subr);
1344                               
1345                        /*
1346                        **  Take care of possible inner calls first.
1347                        **  use a full size temporary buffer to avoid
1348                        **  overflows in rewrite, but strip off the
1349                        **  subroutine call.
1350                        */
1351
1352                        for (i = 2; avp[i] != NULL; i++)
1353                                tpvp[i - 2] = avp[i];
1354                        tpvp[i - 2] = NULL;
1355
1356                        stat = callsubr(tpvp, reclevel, e);
1357                        if (rstat == EX_OK || stat == EX_TEMPFAIL)
1358                                rstat = stat;
1359
1360                        /*
1361                        **  Now we need to call the ruleset specified for
1362                        **  the subroutine. we can do this with the
1363                        **  temporary buffer that we set up earlier,
1364                        **  since it has all the data we want to rewrite.
1365                        */
1366
1367                        stat = rewrite(tpvp, subr, reclevel, e);
1368                        if (rstat == EX_OK || stat == EX_TEMPFAIL)
1369                                rstat = stat;
1370
1371                        /*
1372                        **  Find length of tpvp and current offset into
1373                        **  pvp, if the total is greater than MAXATOM,
1374                        **  then it would overflow the buffer if we copied
1375                        **  it back in to pvp, in which case we throw a
1376                        **  fit.
1377                        */
1378
1379                        for (rvp = tpvp; *rvp != NULL; rvp++)
1380                                continue;
1381                        if (((rvp - tpvp) + (avp - pvp)) > MAXATOM)
1382                        {
1383                                syserr("554 callsubr: expansion too long");
1384                                return EX_DATAERR;
1385                        }
1386
1387                        /*
1388                        **  Now we can copy the rewritten code over
1389                        **  the initial subroutine call in the buffer.
1390                        */
1391
1392                        for (i = 0; tpvp[i] != NULL; i++)
1393                                avp[i] = tpvp[i];
1394                        avp[i] = NULL;
1395
1396                        /*
1397                        **  If we got this far, we've processed the left
1398                        **  most subroutine, and recursively called ourselves
1399                        **  to handle any other subroutines.  We're done.
1400                        */
1401
1402                        break;
1403                }
1404        }
1405        return rstat;
1406}
1407/*
1408**  MAP_LOOKUP -- do lookup in map
1409**
1410**      Parameters:
1411**              map -- the map to use for the lookup.
1412**              key -- the key to look up.
1413**              argvect -- arguments to pass to the map lookup.
1414**              pstat -- a pointer to an integer in which to store the
1415**                      status from the lookup.
1416**              e -- the current envelope.
1417**
1418**      Returns:
1419**              The result of the lookup.
1420**              NULL -- if there was no data for the given key.
1421*/
1422
1423char *
1424map_lookup(map, key, argvect, pstat, e)
1425        STAB *map;
1426        char key[];
1427        char **argvect;
1428        int *pstat;
1429        ENVELOPE *e;
1430{
1431        auto int stat = EX_OK;
1432        char *replac;
1433
1434        if (e->e_sendmode == SM_DEFER)
1435        {
1436                /* don't do any map lookups */
1437                if (tTd(60, 1))
1438                        printf("map_lookup(%s, %s) => DEFERRED\n",
1439                                map->s_name, key);
1440                *pstat = EX_TEMPFAIL;
1441                return NULL;
1442        }
1443        if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags))
1444                return NULL;
1445
1446        if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags))
1447                stripquotes(key);
1448
1449        /* XXX should try to auto-open the map here */
1450
1451        if (tTd(60, 1))
1452        {
1453                printf("map_lookup(%s, %s", map->s_name, key);
1454                if (tTd(60, 5))
1455                {
1456                        int i;
1457
1458                        for (i = 0; argvect[i] != NULL; i++)
1459                                printf(", %%%d=%s", i, argvect[i]);
1460                }
1461                printf(") => ");
1462        }
1463        replac = (*map->s_map.map_class->map_lookup)(&map->s_map,
1464                        key, argvect, &stat);
1465        if (tTd(60, 1))
1466                printf("%s (%d)\n",
1467                        replac != NULL ? replac : "NOT FOUND",
1468                        stat);
1469
1470        /* should recover if stat == EX_TEMPFAIL */
1471        if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags))
1472        {
1473                *pstat = EX_TEMPFAIL;
1474                if (tTd(60, 1))
1475                        printf("map_lookup(%s, %s) tempfail: errno=%d\n",
1476                                map->s_name, key, errno);
1477                if (e->e_message == NULL)
1478                {
1479                        char mbuf[320];
1480
1481                        snprintf(mbuf, sizeof mbuf,
1482                                "%.80s map: lookup (%s): deferred",
1483                                map->s_name,
1484                                shortenstring(key, MAXSHORTSTR));
1485                        e->e_message = newstr(mbuf);
1486                }
1487        }
1488        if (stat == EX_TEMPFAIL && map->s_map.map_tapp != NULL)
1489        {
1490                size_t i = strlen(key) + strlen(map->s_map.map_tapp) + 1;
1491                static char *rwbuf = NULL;
1492                static size_t rwbuflen = 0;
1493
1494                if (i > rwbuflen)
1495                {
1496                        if (rwbuf != NULL)
1497                                free(rwbuf);
1498                        rwbuflen = i;
1499                        rwbuf = (char *) xalloc(rwbuflen);
1500                }
1501                snprintf(rwbuf, rwbuflen, "%s%s", key, map->s_map.map_tapp);
1502                if (tTd(60, 4))
1503                        printf("map_lookup tempfail: returning \"%s\"\n",
1504                                rwbuf);
1505                return rwbuf;
1506        }
1507        return replac;
1508}
1509/*
1510**  BUILDADDR -- build address from token vector.
1511**
1512**      Parameters:
1513**              tv -- token vector.
1514**              a -- pointer to address descriptor to fill.
1515**                      If NULL, one will be allocated.
1516**              flags -- info regarding whether this is a sender or
1517**                      a recipient.
1518**              e -- the current envelope.
1519**
1520**      Returns:
1521**              NULL if there was an error.
1522**              'a' otherwise.
1523**
1524**      Side Effects:
1525**              fills in 'a'
1526*/
1527
1528struct errcodes
1529{
1530        char    *ec_name;               /* name of error code */
1531        int     ec_code;                /* numeric code */
1532} ErrorCodes[] =
1533{
1534        { "usage",              EX_USAGE        },
1535        { "nouser",             EX_NOUSER       },
1536        { "nohost",             EX_NOHOST       },
1537        { "unavailable",        EX_UNAVAILABLE  },
1538        { "software",           EX_SOFTWARE     },
1539        { "tempfail",           EX_TEMPFAIL     },
1540        { "protocol",           EX_PROTOCOL     },
1541#ifdef EX_CONFIG
1542        { "config",             EX_CONFIG       },
1543#endif
1544        { NULL,                 EX_UNAVAILABLE  }
1545};
1546
1547ADDRESS *
1548buildaddr(tv, a, flags, e)
1549        register char **tv;
1550        register ADDRESS *a;
1551        int flags;
1552        register ENVELOPE *e;
1553{
1554        struct mailer **mp;
1555        register struct mailer *m;
1556        register char *p;
1557        char *mname;
1558        char **hostp;
1559        char hbuf[MAXNAME + 1];
1560        static MAILER discardmailer;
1561        static MAILER errormailer;
1562        static char *discardargv[] = { "DISCARD", NULL };
1563        static char *errorargv[] = { "ERROR", NULL };
1564        static char ubuf[MAXNAME + 2];
1565
1566        if (tTd(24, 5))
1567        {
1568                printf("buildaddr, flags=%x, tv=", flags);
1569                printav(tv);
1570        }
1571
1572        if (a == NULL)
1573                a = (ADDRESS *) xalloc(sizeof *a);
1574        bzero((char *) a, sizeof *a);
1575
1576        /* set up default error return flags */
1577        a->q_flags |= DefaultNotify;
1578
1579        if (discardmailer.m_name == NULL)
1580        {
1581                /* initialize the discard mailer */
1582                discardmailer.m_name = "*discard*";
1583                discardmailer.m_mailer = "DISCARD";
1584                discardmailer.m_argv = discardargv;
1585        }
1586
1587        /* figure out what net/mailer to use */
1588        if (*tv == NULL || (**tv & 0377) != CANONNET)
1589        {
1590                syserr("554 buildaddr: no mailer in parsed address");
1591badaddr:
1592                a->q_flags |= QBADADDR;
1593                a->q_mailer = &errormailer;
1594                if (errormailer.m_name == NULL)
1595                {
1596                        /* initialize the bogus mailer */
1597                        errormailer.m_name = "*error*";
1598                        errormailer.m_mailer = "ERROR";
1599                        errormailer.m_argv = errorargv;
1600                }
1601                return a;
1602        }
1603        mname = *++tv;
1604
1605        /* extract host and user portions */
1606        if (*++tv != NULL && (**tv & 0377) == CANONHOST)
1607                hostp = ++tv;
1608        else
1609                hostp = NULL;
1610        while (*tv != NULL && (**tv & 0377) != CANONUSER)
1611                tv++;
1612        if (*tv == NULL)
1613        {
1614                syserr("554 buildaddr: no user");
1615                goto badaddr;
1616        }
1617        if (tv == hostp)
1618                hostp = NULL;
1619        else if (hostp != NULL)
1620                cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1621        cataddr(++tv, NULL, ubuf, sizeof ubuf, ' ');
1622
1623        /* save away the host name */
1624        if (strcasecmp(mname, "error") == 0)
1625        {
1626                if (hostp != NULL)
1627                {
1628                        register struct errcodes *ep;
1629
1630                        if (strchr(hbuf, '.') != NULL)
1631                        {
1632                                extern int dsntoexitstat __P((char *));
1633
1634                                a->q_status = newstr(hbuf);
1635                                setstat(dsntoexitstat(hbuf));
1636                        }
1637                        else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
1638                        {
1639                                setstat(atoi(hbuf));
1640                        }
1641                        else
1642                        {
1643                                for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1644                                        if (strcasecmp(ep->ec_name, hbuf) == 0)
1645                                                break;
1646                                setstat(ep->ec_code);
1647                        }
1648                }
1649                else
1650                        setstat(EX_UNAVAILABLE);
1651                stripquotes(ubuf);
1652                if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
1653                    isascii(ubuf[1]) && isdigit(ubuf[1]) &&
1654                    isascii(ubuf[2]) && isdigit(ubuf[2]) &&
1655                    ubuf[3] == ' ')
1656                {
1657                        char fmt[10];
1658
1659                        strncpy(fmt, ubuf, 3);
1660                        strcpy(&fmt[3], " %s");
1661                        usrerr(fmt, ubuf + 4);
1662
1663                        /*
1664                        **  If this is a 4xx code and we aren't running
1665                        **  SMTP on our input, bounce this message;
1666                        **  otherwise it disappears without a trace.
1667                        */
1668
1669                        if (fmt[0] == '4' && OpMode != MD_SMTP &&
1670                            OpMode != MD_DAEMON)
1671                        {
1672                                e->e_flags |= EF_FATALERRS;
1673                        }
1674                }
1675                else
1676                {
1677                        usrerr("553 %s", ubuf);
1678                }
1679                goto badaddr;
1680        }
1681
1682        for (mp = Mailer; (m = *mp++) != NULL; )
1683        {
1684                if (strcasecmp(m->m_name, mname) == 0)
1685                        break;
1686        }
1687        if (m == NULL)
1688        {
1689                syserr("554 buildaddr: unknown mailer %s", mname);
1690                goto badaddr;
1691        }
1692        a->q_mailer = m;
1693
1694        /* figure out what host (if any) */
1695        if (hostp == NULL)
1696        {
1697                if (!bitnset(M_LOCALMAILER, m->m_flags))
1698                {
1699                        syserr("554 buildaddr: no host");
1700                        goto badaddr;
1701                }
1702                a->q_host = NULL;
1703        }
1704        else
1705                a->q_host = newstr(hbuf);
1706
1707        /* figure out the user */
1708        p = ubuf;
1709        if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
1710        {
1711                p++;
1712                tv++;
1713                a->q_flags |= QNOTREMOTE;
1714        }
1715
1716        /* do special mapping for local mailer */
1717        if (*p == '"')
1718                p++;
1719        if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1720                a->q_mailer = m = ProgMailer;
1721        else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1722                a->q_mailer = m = FileMailer;
1723        else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
1724        {
1725                /* may be :include: */
1726                stripquotes(ubuf);
1727                if (strncasecmp(ubuf, ":include:", 9) == 0)
1728                {
1729                        /* if :include:, don't need further rewriting */
1730                        a->q_mailer = m = InclMailer;
1731                        a->q_user = newstr(&ubuf[9]);
1732                        return a;
1733                }
1734        }
1735
1736        /* rewrite according recipient mailer rewriting rules */
1737        define('h', a->q_host, e);
1738        if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
1739        {
1740                /* sender addresses done later */
1741                (void) rewrite(tv, 2, 0, e);
1742                if (m->m_re_rwset > 0)
1743                       (void) rewrite(tv, m->m_re_rwset, 0, e);
1744        }
1745        (void) rewrite(tv, 4, 0, e);
1746
1747        /* save the result for the command line/RCPT argument */
1748        cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
1749        a->q_user = ubuf;
1750
1751        /*
1752        **  Do mapping to lower case as requested by mailer
1753        */
1754
1755        if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1756                makelower(a->q_host);
1757        if (!bitnset(M_USR_UPPER, m->m_flags))
1758                makelower(a->q_user);
1759
1760        if (tTd(24, 6))
1761        {
1762                printf("buildaddr => ");
1763                printaddr(a, FALSE);
1764        }
1765        return a;
1766}
1767/*
1768**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1769**
1770**      Parameters:
1771**              pvp -- parameter vector to rebuild.
1772**              evp -- last parameter to include.  Can be NULL to
1773**                      use entire pvp.
1774**              buf -- buffer to build the string into.
1775**              sz -- size of buf.
1776**              spacesub -- the space separator character; if null,
1777**                      use SpaceSub.
1778**
1779**      Returns:
1780**              none.
1781**
1782**      Side Effects:
1783**              Destroys buf.
1784*/
1785
1786void
1787cataddr(pvp, evp, buf, sz, spacesub)
1788        char **pvp;
1789        char **evp;
1790        char *buf;
1791        register int sz;
1792        int spacesub;
1793{
1794        bool oatomtok = FALSE;
1795        bool natomtok = FALSE;
1796        register int i;
1797        register char *p;
1798
1799        if (spacesub == '\0')
1800                spacesub = SpaceSub;
1801
1802        if (pvp == NULL)
1803        {
1804                (void) strcpy(buf, "");
1805                return;
1806        }
1807        p = buf;
1808        sz -= 2;
1809        while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1810        {
1811                natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
1812                if (oatomtok && natomtok)
1813                        *p++ = spacesub;
1814                (void) strcpy(p, *pvp);
1815                oatomtok = natomtok;
1816                p += i;
1817                sz -= i + 1;
1818                if (pvp++ == evp)
1819                        break;
1820        }
1821        *p = '\0';
1822}
1823/*
1824**  SAMEADDR -- Determine if two addresses are the same
1825**
1826**      This is not just a straight comparison -- if the mailer doesn't
1827**      care about the host we just ignore it, etc.
1828**
1829**      Parameters:
1830**              a, b -- pointers to the internal forms to compare.
1831**
1832**      Returns:
1833**              TRUE -- they represent the same mailbox.
1834**              FALSE -- they don't.
1835**
1836**      Side Effects:
1837**              none.
1838*/
1839
1840bool
1841sameaddr(a, b)
1842        register ADDRESS *a;
1843        register ADDRESS *b;
1844{
1845        register ADDRESS *ca, *cb;
1846
1847        /* if they don't have the same mailer, forget it */
1848        if (a->q_mailer != b->q_mailer)
1849                return (FALSE);
1850
1851        /* if the user isn't the same, we can drop out */
1852        if (strcmp(a->q_user, b->q_user) != 0)
1853                return (FALSE);
1854
1855        /* if we have good uids for both but they differ, these are different */
1856        if (a->q_mailer == ProgMailer)
1857        {
1858                ca = getctladdr(a);
1859                cb = getctladdr(b);
1860                if (ca != NULL && cb != NULL &&
1861                    bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1862                    ca->q_uid != cb->q_uid)
1863                        return (FALSE);
1864        }
1865
1866        /* otherwise compare hosts (but be careful for NULL ptrs) */
1867        if (a->q_host == b->q_host)
1868        {
1869                /* probably both null pointers */
1870                return (TRUE);
1871        }
1872        if (a->q_host == NULL || b->q_host == NULL)
1873        {
1874                /* only one is a null pointer */
1875                return (FALSE);
1876        }
1877        if (strcmp(a->q_host, b->q_host) != 0)
1878                return (FALSE);
1879
1880        return (TRUE);
1881}
1882/*
1883**  PRINTADDR -- print address (for debugging)
1884**
1885**      Parameters:
1886**              a -- the address to print
1887**              follow -- follow the q_next chain.
1888**
1889**      Returns:
1890**              none.
1891**
1892**      Side Effects:
1893**              none.
1894*/
1895
1896struct qflags
1897{
1898        char    *qf_name;
1899        u_long  qf_bit;
1900};
1901
1902struct qflags   AddressFlags[] =
1903{
1904        { "QDONTSEND",          QDONTSEND       },
1905        { "QBADADDR",           QBADADDR        },
1906        { "QGOODUID",           QGOODUID        },
1907        { "QPRIMARY",           QPRIMARY        },
1908        { "QQUEUEUP",           QQUEUEUP        },
1909        { "QSENT",              QSENT           },
1910        { "QNOTREMOTE",         QNOTREMOTE      },
1911        { "QSELFREF",           QSELFREF        },
1912        { "QVERIFIED",          QVERIFIED       },
1913        { "QBOGUSSHELL",        QBOGUSSHELL     },
1914        { "QUNSAFEADDR",        QUNSAFEADDR     },
1915        { "QPINGONSUCCESS",     QPINGONSUCCESS  },
1916        { "QPINGONFAILURE",     QPINGONFAILURE  },
1917        { "QPINGONDELAY",       QPINGONDELAY    },
1918        { "QHASNOTIFY",         QHASNOTIFY      },
1919        { "QRELAYED",           QRELAYED        },
1920        { "QEXPANDED",          QEXPANDED       },
1921        { "QDELIVERED",         QDELIVERED      },
1922        { "QDELAYED",           QDELAYED        },
1923        { "QTHISPASS",          QTHISPASS       },
1924        { "QRCPTOK",            QRCPTOK         },
1925        { NULL }
1926};
1927
1928void
1929printaddr(a, follow)
1930        register ADDRESS *a;
1931        bool follow;
1932{
1933        register MAILER *m;
1934        MAILER pseudomailer;
1935        register struct qflags *qfp;
1936        bool firstone;
1937
1938        if (a == NULL)
1939        {
1940                printf("[NULL]\n");
1941                return;
1942        }
1943
1944        while (a != NULL)
1945        {
1946                printf("%lx=", (u_long) a);
1947                (void) fflush(stdout);
1948
1949                /* find the mailer -- carefully */
1950                m = a->q_mailer;
1951                if (m == NULL)
1952                {
1953                        m = &pseudomailer;
1954                        m->m_mno = -1;
1955                        m->m_name = "NULL";
1956                }
1957
1958                printf("%s:\n\tmailer %d (%s), host `%s'\n",
1959                       a->q_paddr == NULL ? "<null>" : a->q_paddr,
1960                       m->m_mno, m->m_name,
1961                       a->q_host == NULL ? "<null>" : a->q_host);
1962                printf("\tuser `%s', ruser `%s'\n",
1963                       a->q_user,
1964                       a->q_ruser == NULL ? "<null>" : a->q_ruser);
1965                printf("\tnext=%lx, alias %lx, uid %d, gid %d\n",
1966                       (u_long) a->q_next, (u_long) a->q_alias,
1967                       (int) a->q_uid, (int) a->q_gid);
1968                printf("\tflags=%lx<", a->q_flags);
1969                firstone = TRUE;
1970                for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
1971                {
1972                        if (!bitset(qfp->qf_bit, a->q_flags))
1973                                continue;
1974                        if (!firstone)
1975                                printf(",");
1976                        firstone = FALSE;
1977                        printf("%s", qfp->qf_name);
1978                }
1979                printf(">\n");
1980                printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n",
1981                       a->q_owner == NULL ? "(none)" : a->q_owner,
1982                       a->q_home == NULL ? "(none)" : a->q_home,
1983                       a->q_fullname == NULL ? "(none)" : a->q_fullname);
1984                printf("\torcpt=\"%s\", statmta=%s, status=%s\n",
1985                       a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
1986                       a->q_statmta == NULL ? "(none)" : a->q_statmta,
1987                       a->q_status == NULL ? "(none)" : a->q_status);
1988                printf("\trstatus=\"%s\"\n",
1989                       a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
1990                printf("\tspecificity=%d, statdate=%s\n",
1991                        a->q_specificity, ctime(&a->q_statdate));
1992
1993                if (!follow)
1994                        return;
1995                a = a->q_next;
1996        }
1997}
1998/*
1999**  EMPTYADDR -- return TRUE if this address is empty (``<>'')
2000**
2001**      Parameters:
2002**              a -- pointer to the address
2003**
2004**      Returns:
2005**              TRUE -- if this address is "empty" (i.e., no one should
2006**                      ever generate replies to it.
2007**              FALSE -- if it is a "regular" (read: replyable) address.
2008*/
2009
2010bool
2011emptyaddr(a)
2012        register ADDRESS *a;
2013{
2014        return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2015               a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2016}
2017/*
2018**  REMOTENAME -- return the name relative to the current mailer
2019**
2020**      Parameters:
2021**              name -- the name to translate.
2022**              m -- the mailer that we want to do rewriting relative
2023**                      to.
2024**              flags -- fine tune operations.
2025**              pstat -- pointer to status word.
2026**              e -- the current envelope.
2027**
2028**      Returns:
2029**              the text string representing this address relative to
2030**                      the receiving mailer.
2031**
2032**      Side Effects:
2033**              none.
2034**
2035**      Warnings:
2036**              The text string returned is tucked away locally;
2037**                      copy it if you intend to save it.
2038*/
2039
2040char *
2041remotename(name, m, flags, pstat, e)
2042        char *name;
2043        struct mailer *m;
2044        int flags;
2045        int *pstat;
2046        register ENVELOPE *e;
2047{
2048        register char **pvp;
2049        char *fancy;
2050        char *oldg = macvalue('g', e);
2051        int rwset;
2052        static char buf[MAXNAME + 1];
2053        char lbuf[MAXNAME + 1];
2054        char pvpbuf[PSBUFSIZE];
2055        extern char *crackaddr __P((char *));
2056
2057        if (tTd(12, 1))
2058                printf("remotename(%s)\n", name);
2059
2060        /* don't do anything if we are tagging it as special */
2061        if (bitset(RF_SENDERADDR, flags))
2062                rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2063                                                     : m->m_se_rwset;
2064        else
2065                rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2066                                                     : m->m_re_rwset;
2067        if (rwset < 0)
2068                return (name);
2069
2070        /*
2071        **  Do a heuristic crack of this name to extract any comment info.
2072        **      This will leave the name as a comment and a $g macro.
2073        */
2074
2075        if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2076                fancy = "\201g";
2077        else
2078                fancy = crackaddr(name);
2079
2080        /*
2081        **  Turn the name into canonical form.
2082        **      Normally this will be RFC 822 style, i.e., "user@domain".
2083        **      If this only resolves to "user", and the "C" flag is
2084        **      specified in the sending mailer, then the sender's
2085        **      domain will be appended.
2086        */
2087
2088        pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2089        if (pvp == NULL)
2090                return (name);
2091        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2092                *pstat = EX_TEMPFAIL;
2093        if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2094        {
2095                /* append from domain to this address */
2096                register char **pxp = pvp;
2097
2098                /* see if there is an "@domain" in the current name */
2099                while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2100                        pxp++;
2101                if (*pxp == NULL)
2102                {
2103                        /* no.... append the "@domain" from the sender */
2104                        register char **qxq = e->e_fromdomain;
2105
2106                        while ((*pxp++ = *qxq++) != NULL)
2107                                continue;
2108                        if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
2109                                *pstat = EX_TEMPFAIL;
2110                }
2111        }
2112
2113        /*
2114        **  Do more specific rewriting.
2115        **      Rewrite using ruleset 1 or 2 depending on whether this is
2116        **              a sender address or not.
2117        **      Then run it through any receiving-mailer-specific rulesets.
2118        */
2119
2120        if (bitset(RF_SENDERADDR, flags))
2121        {
2122                if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL)
2123                        *pstat = EX_TEMPFAIL;
2124        }
2125        else
2126        {
2127                if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL)
2128                        *pstat = EX_TEMPFAIL;
2129        }
2130        if (rwset > 0)
2131        {
2132                if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
2133                        *pstat = EX_TEMPFAIL;
2134        }
2135
2136        /*
2137        **  Do any final sanitation the address may require.
2138        **      This will normally be used to turn internal forms
2139        **      (e.g., user@host.LOCAL) into external form.  This
2140        **      may be used as a default to the above rules.
2141        */
2142
2143        if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
2144                *pstat = EX_TEMPFAIL;
2145
2146        /*
2147        **  Now restore the comment information we had at the beginning.
2148        */
2149
2150        cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
2151        define('g', lbuf, e);
2152
2153        /* need to make sure route-addrs have <angle brackets> */
2154        if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2155                expand("<\201g>", buf, sizeof buf, e);
2156        else
2157                expand(fancy, buf, sizeof buf, e);
2158
2159        define('g', oldg, e);
2160
2161        if (tTd(12, 1))
2162                printf("remotename => `%s'\n", buf);
2163        return (buf);
2164}
2165/*
2166**  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2167**
2168**      Parameters:
2169**              a -- the address to map (but just the user name part).
2170**              sendq -- the sendq in which to install any replacement
2171**                      addresses.
2172**              aliaslevel -- the alias nesting depth.
2173**              e -- the envelope.
2174**
2175**      Returns:
2176**              none.
2177*/
2178
2179#define Q_COPYFLAGS     (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2180                         Q_PINGFLAGS|QHASNOTIFY|\
2181                         QRELAYED|QEXPANDED|QDELIVERED|QDELAYED)
2182
2183void
2184maplocaluser(a, sendq, aliaslevel, e)
2185        register ADDRESS *a;
2186        ADDRESS **sendq;
2187        int aliaslevel;
2188        ENVELOPE *e;
2189{
2190        register char **pvp;
2191        register ADDRESS *a1 = NULL;
2192        auto char *delimptr;
2193        char pvpbuf[PSBUFSIZE];
2194
2195        if (tTd(29, 1))
2196        {
2197                printf("maplocaluser: ");
2198                printaddr(a, FALSE);
2199        }
2200        pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
2201        if (pvp == NULL)
2202        {
2203                if (tTd(29, 9))
2204                        printf("maplocaluser: cannot prescan %s\n", a->q_user);
2205                return;
2206        }
2207
2208        define('h', a->q_host, e);
2209        define('u', a->q_user, e);
2210        define('z', a->q_home, e);
2211 
2212        if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
2213        {
2214                if (tTd(29, 9))
2215                        printf("maplocaluser: rewrite tempfail\n");
2216                a->q_flags |= QQUEUEUP;
2217                a->q_status = "4.4.3";
2218                return;
2219        }
2220        if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2221        {
2222                if (tTd(29, 9))
2223                        printf("maplocaluser: doesn't resolve\n");
2224                return;
2225        }
2226
2227        /* if non-null, mailer destination specified -- has it changed? */
2228        a1 = buildaddr(pvp, NULL, 0, e);
2229        if (a1 == NULL || sameaddr(a, a1))
2230        {
2231                if (tTd(29, 9))
2232                        printf("maplocaluser: address unchanged\n");
2233                if (a1 != NULL)
2234                        free(a1);
2235                return;
2236        }
2237
2238        /* make new address take on flags and print attributes of old */
2239        a1->q_flags &= ~Q_COPYFLAGS;
2240        a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2241        a1->q_paddr = a->q_paddr;
2242
2243        /* mark old address as dead; insert new address */
2244        a->q_flags |= QDONTSEND;
2245        if (tTd(29, 5))
2246        {
2247                printf("maplocaluser: QDONTSEND ");
2248                printaddr(a, FALSE);
2249        }
2250        a1->q_alias = a;
2251        allocaddr(a1, RF_COPYALL, a->q_paddr);
2252        (void) recipient(a1, sendq, aliaslevel, e);
2253}
2254/*
2255**  DEQUOTE_INIT -- initialize dequote map
2256**
2257**      This is a no-op.
2258**
2259**      Parameters:
2260**              map -- the internal map structure.
2261**              args -- arguments.
2262**
2263**      Returns:
2264**              TRUE.
2265*/
2266
2267bool
2268dequote_init(map, args)
2269        MAP *map;
2270        char *args;
2271{
2272        register char *p = args;
2273
2274        map->map_mflags |= MF_KEEPQUOTES;
2275        for (;;)
2276        {
2277                while (isascii(*p) && isspace(*p))
2278                        p++;
2279                if (*p != '-')
2280                        break;
2281                switch (*++p)
2282                {
2283                  case 'a':
2284                        map->map_app = ++p;
2285                        break;
2286
2287                  case 's':
2288                        map->map_coldelim = *++p;
2289                        break;
2290                }
2291                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2292                        p++;
2293                if (*p != '\0')
2294                        *p = '\0';
2295        }
2296        if (map->map_app != NULL)
2297                map->map_app = newstr(map->map_app);
2298
2299        return TRUE;
2300}
2301/*
2302**  DEQUOTE_MAP -- unquote an address
2303**
2304**      Parameters:
2305**              map -- the internal map structure (ignored).
2306**              name -- the name to dequote.
2307**              av -- arguments (ignored).
2308**              statp -- pointer to status out-parameter.
2309**
2310**      Returns:
2311**              NULL -- if there were no quotes, or if the resulting
2312**                      unquoted buffer would not be acceptable to prescan.
2313**              else -- The dequoted buffer.
2314*/
2315
2316/* ARGSUSED2 */
2317char *
2318dequote_map(map, name, av, statp)
2319        MAP *map;
2320        char *name;
2321        char **av;
2322        int *statp;
2323{
2324        register char *p;
2325        register char *q;
2326        register char c;
2327        int anglecnt = 0;
2328        int cmntcnt = 0;
2329        int quotecnt = 0;
2330        int spacecnt = 0;
2331        bool quotemode = FALSE;
2332        bool bslashmode = FALSE;
2333        char spacesub = map->map_coldelim;
2334
2335        for (p = q = name; (c = *p++) != '\0'; )
2336        {
2337                if (bslashmode)
2338                {
2339                        bslashmode = FALSE;
2340                        *q++ = c;
2341                        continue;
2342                }
2343
2344                if (c == ' ' && spacesub != '\0')
2345                        c = spacesub;
2346
2347                switch (c)
2348                {
2349                  case '\\':
2350                        bslashmode = TRUE;
2351                        break;
2352
2353                  case '(':
2354                        cmntcnt++;
2355                        break;
2356
2357                  case ')':
2358                        if (cmntcnt-- <= 0)
2359                                return NULL;
2360                        break;
2361
2362                  case ' ':
2363                        spacecnt++;
2364                        break;
2365                }
2366
2367                if (cmntcnt > 0)
2368                {
2369                        *q++ = c;
2370                        continue;
2371                }
2372
2373                switch (c)
2374                {
2375                  case '"':
2376                        quotemode = !quotemode;
2377                        quotecnt++;
2378                        continue;
2379
2380                  case '<':
2381                        anglecnt++;
2382                        break;
2383
2384                  case '>':
2385                        if (anglecnt-- <= 0)
2386                                return NULL;
2387                        break;
2388                }
2389                *q++ = c;
2390        }
2391
2392        if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
2393            quotemode || quotecnt <= 0 || spacecnt != 0)
2394                return NULL;
2395        *q++ = '\0';
2396        return map_rewrite(map, name, strlen(name), NULL);
2397}
2398/*
2399**  RSCHECK -- check string(s) for validity using rewriting sets
2400**
2401**      Parameters:
2402**              rwset -- the rewriting set to use.
2403**              p1 -- the first string to check.
2404**              p2 -- the second string to check -- may be null.
2405**              e -- the current envelope.
2406**
2407**      Returns:
2408**              EX_OK -- if the rwset doesn't resolve to $#error
2409**              else -- the failure status (message printed)
2410*/
2411
2412int
2413rscheck(rwset, p1, p2, e)
2414        char *rwset;
2415        char *p1;
2416        char *p2;
2417        ENVELOPE *e;
2418{
2419        char *buf;
2420        int bufsize;
2421        int saveexitstat;
2422        int rstat = EX_OK;
2423        char **pvp;
2424        int rsno;
2425        bool discard = FALSE;
2426        auto ADDRESS a1;
2427        bool saveQuickAbort = QuickAbort;
2428        bool saveSuprErrs = SuprErrs;
2429        char buf0[MAXLINE];
2430        char pvpbuf[PSBUFSIZE];
2431        extern char MsgBuf[];
2432
2433        if (tTd(48, 2))
2434                printf("rscheck(%s, %s, %s)\n", rwset, p1,
2435                        p2 == NULL ? "(NULL)" : p2);
2436
2437        rsno = strtorwset(rwset, NULL, ST_FIND);
2438        if (rsno < 0)
2439                return EX_OK;
2440
2441        if (p2 != NULL)
2442        {
2443                bufsize = strlen(p1) + strlen(p2) + 2;
2444                if (bufsize > sizeof buf0)
2445                        buf = xalloc(bufsize);
2446                else
2447                {
2448                        buf = buf0;
2449                        bufsize = sizeof buf0;
2450                }
2451                (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
2452        }
2453        else
2454        {
2455                bufsize = strlen(p1) + 1;
2456                if (bufsize > sizeof buf0)
2457                        buf = xalloc(bufsize);
2458                else
2459                {
2460                        buf = buf0;
2461                        bufsize = sizeof buf0;
2462                }
2463                (void) snprintf(buf, bufsize, "%s", p1);
2464        }
2465        SuprErrs = TRUE;
2466        QuickAbort = FALSE;
2467        pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
2468        SuprErrs = saveSuprErrs;
2469        if (pvp == NULL)
2470        {
2471                if (tTd(48, 2))
2472                        printf("rscheck: cannot prescan input\n");
2473/*
2474                syserr("rscheck: cannot prescan input: \"%s\"",
2475                        shortenstring(buf, MAXSHORTSTR));
2476                rstat = EX_DATAERR;
2477*/
2478                goto finis;
2479        }
2480        (void) rewrite(pvp, rsno, 0, e);
2481        if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
2482            pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
2483                               strcmp(pvp[1], "discard") != 0))
2484        {
2485                goto finis;
2486        }
2487
2488        if (strcmp(pvp[1], "discard") == 0)
2489        {
2490                if (tTd(48, 2))
2491                        printf("rscheck: discard mailer selected\n");
2492                e->e_flags |= EF_DISCARD;
2493                discard = TRUE;
2494        }
2495        else
2496        {
2497                int savelogusrerrs = LogUsrErrs;
2498                static bool logged = FALSE;
2499
2500                /* got an error -- process it */
2501                saveexitstat = ExitStat;
2502                LogUsrErrs = FALSE;
2503                (void) buildaddr(pvp, &a1, 0, e);
2504                LogUsrErrs = savelogusrerrs;
2505                rstat = ExitStat;
2506                ExitStat = saveexitstat;
2507                if (!logged)
2508                {
2509                        markstats(e, &a1, TRUE);
2510                        logged = TRUE;
2511                }
2512        }
2513       
2514        if (LogLevel >= 4)
2515        {
2516                char *relay;
2517                char *p;
2518                char lbuf[MAXLINE];
2519
2520                p = lbuf;
2521                if (p2 != NULL)
2522                {
2523                        snprintf(p, SPACELEFT(lbuf, p),
2524                                ", arg2=%s",
2525                                p2);
2526                        p += strlen(p);
2527                }
2528                if ((relay = macvalue('_', e)) != NULL)
2529                {
2530                        snprintf(p, SPACELEFT(lbuf, p),
2531                                ", relay=%s", relay);
2532                        p += strlen(p);
2533                }
2534                *p = '\0';
2535                if (discard)
2536                        sm_syslog(LOG_NOTICE, e->e_id,
2537                                  "ruleset=%s, arg1=%s%s, discard",
2538                                  rwset, p1, lbuf);
2539                else
2540                        sm_syslog(LOG_NOTICE, e->e_id,
2541                                  "ruleset=%s, arg1=%s%s, reject=%s",
2542                                  rwset, p1, lbuf, MsgBuf);
2543        }
2544
2545 finis:
2546        /* clean up */
2547        QuickAbort = saveQuickAbort;
2548        setstat(rstat);
2549        if (buf != buf0)
2550                free(buf);
2551
2552        if (rstat != EX_OK && QuickAbort)
2553                longjmp(TopFrame, 2);
2554        return rstat;
2555}
Note: See TracBrowser for help on using the repository browser.