home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / auucp+-1.02 / fuucp_plus_src.lzh / sendmail / addr.c next >
Encoding:
C/C++ Source or Header  |  1991-09-28  |  30.8 KB  |  1,200 lines

  1. /**********************************************************************/
  2. /*                                                                    */
  3. /*  Stolen from smail. See addr.h for copyright notices               */
  4. /*                                                                    */
  5. /*                                 -                                  */
  6. /* addr.c:                                                            */
  7. /* routines to parse addresses                                        */
  8. /*                                                                    */
  9. /* external functions:  preparse_address, parse_address,              */
  10. /*                      build_uucp_route, build_partial_uucp_route,   */
  11. /*                      address_token, back_address_token, alloc_addr,*/
  12. /*                      insert_addr_list, addr_sort                   */
  13. /*                                                                    */
  14. /**********************************************************************/
  15.  
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <ctype.h>
  19.  
  20. #include "smail.h"
  21. #include "addr.h"
  22. #include "dys.h"
  23. #include "exitcodes.h"
  24.  
  25. extern char *preparse_address();
  26. extern int parse_address();
  27. extern char *address_token();
  28. extern char *back_address_token();
  29. extern char *build_uucp_route();
  30. extern char *build_partial_uucp_route();
  31. extern struct addr *alloc_addr();
  32. extern void insert_addr_list();
  33. extern struct addr *addr_sort();
  34. extern struct error *note_error();
  35.  
  36. /* external functions defined in alloc.c */
  37. extern char *xmalloc();
  38. extern char *xrealloc();
  39. extern char *bmalloc();
  40. extern char *brealloc();
  41. extern void bfree();
  42. extern struct block *alloc_block();
  43. extern void realloc_block();
  44. extern void free_block();
  45.  
  46. /* functions local to this file */
  47. void str_cat();
  48. static int check_target_and_remainder();
  49. static char *escaped();
  50. static char *internal_build_uucp_route();
  51. static int addrcmp();
  52.  
  53.  
  54. /*
  55.  * preparse_address - do preliminary parsing that might be needed for address
  56.  *
  57.  * this routine should be used when an address is first extracted from a
  58.  * source.  It transforms some mutant addressing forms into something more
  59.  * managable.
  60.  *
  61.  * Transformations:
  62.  *
  63.  *    <string>        becomes just  string (recursively)
  64.  *    host!(host!)*@route    becomes a pure !-route
  65.  *
  66.  * NOTE:  We don't handle @route:host!(host!)*@route, for now.  Maybe later.
  67.  *
  68.  * input:
  69.  *    address    - address to be preparsed
  70.  *    error    - error message
  71.  *
  72.  * output:
  73.  *    parsed address, or NULL for parsing error, message returned in error
  74.  *    output is guarranteed to not be a pointer to the input
  75.  */
  76. char *
  77. preparse_address(address, error)
  78.     char *address;            /* address to be preparsed */
  79.     char **error;            /* return error message here */
  80. {
  81.     register char *ap;            /* temp for scanning address */
  82.     char *mark_start = NULL;        /* marked position of < */
  83.     char *mark_end = NULL;        /* marked position of > */
  84.     int nest_cnt = 0;            /* nesting count for angle brackets */
  85.  
  86.     /*
  87.      * scan for < and > pairs and find the last or innermost matching
  88.      * pair.
  89.      */
  90.     for (ap = address; ap && *ap; ap = address_token(ap)) {
  91.     if (*ap == '<') {
  92.         nest_cnt++;
  93.         mark_start = ap + 1;
  94.         mark_end = NULL;
  95.     } else if (*ap == '>') {
  96.         nest_cnt--;
  97.         if (mark_end == NULL) {
  98.         mark_end = ap;
  99.         }
  100.     }
  101.     }
  102.     if (ap == NULL) {
  103.     *error = "bad address token";
  104.     return NULL;
  105.     }
  106.     if (mark_start && mark_end == NULL) {
  107.     /* hmm, no match for the < token */
  108.     *error = "no match for `<' in address";
  109.     return NULL;
  110.     }
  111.     if (nest_cnt != 0) {
  112.     if (nest_cnt < 0) {
  113.         *error = "no match for > in address";
  114.     } else {
  115.         *error = "no match for < in address";
  116.     }
  117.     return NULL;
  118.     }
  119.     /* narrow to the route-addr */
  120.     if (mark_end) {
  121.     *mark_end = '\0';
  122.     address = ap = mark_start;
  123.     }
  124.  
  125.     /*
  126.      * now search for the mutant form: path!@route-addr
  127.      */
  128.     if (*ap == '@') {
  129.     /* valid route-addr, not a mutant one */
  130.     ap = xmalloc((unsigned)(strlen(address) + 1));
  131.     (void) strcpy(ap, address);
  132.     if (mark_end) {
  133.         *mark_end = '>';        /* widden the original address */
  134.     }
  135.     return ap;            /*  no transformations */
  136.     }
  137.  
  138.     while (*ap) {
  139.     ap = address_token(ap);
  140.     if (ap == NULL) {
  141.         *error = "bad address token";
  142.         return NULL;
  143.     }
  144.     if (*ap != '!') {
  145.         ap = xmalloc((unsigned)(strlen(address) + 1));
  146.         (void) strcpy(ap, address);
  147.         if (mark_end) {
  148.         *mark_end = '>';    /* widden the original address */
  149.         }
  150.         return ap;        /* address should be okay */
  151.     }
  152.     ap++;
  153.     if (*ap == '@') {
  154.         /* matched host!(host!)*@route -- build the !-route */
  155.         register char *p = xmalloc((unsigned)strlen(address));
  156.         /* first part already !-route */
  157.         (void) strncpy(p, address, ap-address);
  158.         if (mark_end) {
  159.         *mark_end = '>';    /* widden the original address */
  160.         }
  161.         ap = build_uucp_route(ap, error); /* build !-route */
  162.         if (ap == NULL) {
  163.         return NULL;
  164.         }
  165.         (void) strcat(p, ap);    /* concatenate together */
  166.         free(ap);
  167.         return p;            /* transformed */
  168.     }
  169.     }
  170.     ap = xmalloc((unsigned)(strlen(address) + 1));
  171.     (void) strcpy(ap, address);
  172.     if (mark_end) {
  173.     *mark_end = '>';    /* widden the original address */
  174.     }
  175.     return ap;                /* no transformations */
  176. }
  177.  
  178.  
  179. /*
  180.  * parse_address - extract a target and remainder from an address
  181.  *
  182.  * using the rules in section 3.2 of the mailer.design document,
  183.  * extract a target and a remainder from an address.
  184.  *
  185.  * The target is defined as the first destination host in an address,
  186.  * the remainder is defined as the remaining parat of the address
  187.  * after extracting the target.
  188.  *
  189.  * A short form of the rules for extraction is the following table
  190.  * of addressing forms in order of precedence:
  191.  *
  192.  *    +---------------------------------------------------------------+
  193.  *    | form            | description        | return    |
  194.  *    |-----------------------|-----------------------|---------------|
  195.  *    | @target,remainder    | route from route-addr    | RFC_ROUTE    |
  196.  *    | @target:remainder    | route from route-addr    | RFC_ENDROUTE    |
  197.  *    | remainder@target    | standard mailbox    | MAILBOX    |
  198.  *    | target!remainder    | UUCP !-route        | UUCP_ROUTE    |
  199.  *    | target::remainder    | decnet route        | DECNET    |
  200.  *    | target:remainder    | obsolete berkenet    | BERKNET    |
  201.  *    | remainder%target    | obsolete mailbox hack    | PCT_MAILBOX    |
  202.  *    | remainder        | local address form    | LOCAL        |
  203.  *    +---------------------------------------------------------------+
  204.  *
  205.  * inputs:
  206.  *    address    - string containing the address to be parsed
  207.  *    target    - where to store pointer to computed target
  208.  *    remainder - where to store pointer to computed target
  209.  *
  210.  * outut:
  211.  *    return the address form as described in the above table.  Also,
  212.  *    return in target a pointer to to the target and return in
  213.  *    remainder a pointer to the remainder.  If an error is detected
  214.  *    return FAIL and load the remainder with an error message.
  215.  *    If target is NULL, then only a form is returned, a target and
  216.  *    remainder are not returned, though an error message may still
  217.  *    be loaded into remainder.
  218.  *
  219.  * NOTE:  address will be overwritten unless it is in local form, or
  220.  *      a target and remainder are not returned.
  221.  *
  222.  * calls: address_token, back_address_token
  223.  * called by: build_uucp_route
  224.  */
  225. int
  226. parse_address(address, target, remainder)
  227.     char *address;            /* address to parse (destructively) */
  228.     char **target;            /* store pointer to target host here */
  229.     char **remainder;            /* store pointer to remainder here */
  230. {
  231.     char *ep;                /* pointer to end of address */
  232.     register char *last_tokens;        /* start of second to last token */
  233.     register char *ap;            /* pointer for scanning address */
  234.     register char *p;            /* temp */
  235.  
  236.     /*
  237.      * make sure we have an address
  238.      */
  239.     ap = address;
  240.     if (*ap == '\0') {
  241.     /* nothing to do with a zero-length address */
  242.     *remainder = "null address";
  243.     return FAIL;
  244.     }
  245.  
  246.     /*
  247.      * does the address begin with @target[,:] ?
  248.      */
  249.     if (*ap == '@') {
  250.     if (target) {
  251.         *target = ap + 1;            /* mark the target */
  252.     }
  253.     ap = address_token(ap + 1);        /* skip target */
  254.     if (ap == NULL) {
  255.         *remainder = "bad address token";
  256.         return FAIL;
  257.     }
  258.  
  259.     /* ensure that the `,' or `:' is in the address */
  260.     if (!ap) {
  261.         /* interesting, address just contained '@' */
  262.         *remainder = "syntax error:  no target host";
  263.         return FAIL;
  264.     }
  265.     if (*ap == ',' || *ap == ':') {
  266.         int retval = (*ap==','? RFC_ROUTE: RFC_ENDROUTE);
  267.         if (target) {
  268.         *ap++ = '\0';        /* null terminate target */
  269.         *remainder = ap;
  270.         if (check_target_and_remainder(target, remainder) == FAIL) {
  271.             return FAIL;
  272.         }
  273.         }
  274.         return retval;
  275.     }
  276.     /* we have a syntax error, missing , or : */
  277.     *remainder = "syntax error: , or : missing in route-addr";
  278.     return FAIL;
  279.     }
  280.  
  281.     /*
  282.      * is the address a standard mailbox ?
  283.      * i.e., does the address end in @target ?
  284.      */
  285.     ep = address + strlen(address);
  286.     last_tokens = back_address_token(ap, ep);
  287.     if (last_tokens && last_tokens > ap) {
  288.     last_tokens = back_address_token(ap, last_tokens);
  289.     }
  290.     if (last_tokens == NULL) {
  291.     *remainder = "bad address token";
  292.     return FAIL;
  293.     }
  294.     if (last_tokens > ap && *last_tokens == '@') {
  295.     /* it matches @token, null terminate the remainder and finish up */
  296.     if (target) {
  297.         *last_tokens = '\0';
  298.         *target = last_tokens+1;
  299.         *remainder = ap;
  300.         if (check_target_and_remainder(target, remainder) == FAIL) {
  301.         return FAIL;
  302.         }
  303.     }
  304.     return MAILBOX;
  305.     }
  306.  
  307.     /*
  308.      * is the address a UUCP !-route ?
  309.      * i.e., does the address begin with target! ?
  310.      */
  311.     p = address_token(ap);
  312.     if (p && *p == '!') {
  313.     /* it matches target!, null terminate target and finish up */
  314.     if (target) {
  315.         *p = '\0';
  316.         *target = ap;
  317.         *remainder = p+1;
  318.         if (check_target_and_remainder(target, remainder) == FAIL) {
  319.         return FAIL;
  320.         }
  321.     }
  322.     return UUCP_ROUTE;
  323.     }
  324.  
  325.     /*
  326.      * is the address a BERKENET or DECNET syntax?
  327.      */
  328.     if (p && *p == ':') {
  329.     if (*(p + 1) == ':') {
  330.         /* DECNET syntax */
  331.         if (target) {
  332.         *p = '\0';
  333.         *target = ap;
  334.         *remainder = p + 2;
  335.         if (check_target_and_remainder(target, remainder) == FAIL) {
  336.             return FAIL;
  337.         }
  338.         }
  339.         return DECNET;
  340.     }
  341.     /* Berkenet syntax */
  342.     if (target) {
  343.         *p = '\0';
  344.         *target = ap;
  345.         *remainder = p + 1;
  346.         if (check_target_and_remainder(target, remainder) == FAIL) {
  347.         return FAIL;
  348.         }
  349.     }
  350.     return BERKENET;
  351.     }
  352.  
  353.     /*
  354.      * is the address a non-standard mailbox ?
  355.      * i.e., does the address end in %target ?
  356.      */
  357.     if (last_tokens && last_tokens - ap > 0 && *last_tokens == '%') {
  358.     /* it matches @target, null terminate the remainder and finish up */
  359.     if (target) {
  360.         *last_tokens = '\0';
  361.         *target = last_tokens+1;
  362.         *remainder = ap;
  363.         if (check_target_and_remainder(target, remainder) == FAIL) {
  364.         return FAIL;
  365.         }
  366.     }
  367.     return PCT_MAILBOX;
  368.     }
  369.  
  370.     /*
  371.      * we have a local form address
  372.      */
  373.     if (target) {
  374.     *target = NULL;
  375.     *remainder = ap;
  376.     }
  377.     return LOCAL;
  378. }
  379.  
  380. /* check_target_and_remainder - check for glaring problems */
  381. static int
  382. check_target_and_remainder(target, remainder)
  383.     char **target;
  384.     char **remainder;
  385. {
  386.     register int c;
  387.     register char *p;
  388.  
  389.     if ((*remainder)[0] == '\0') {
  390.     *remainder = "no remainder address";
  391.     }
  392.     /* the set of chars in the target should be limited to a small set */
  393.     p = *target;
  394.     if (*p == '-') {
  395.     *remainder = "target cannot begin with `-'";
  396.     return FAIL;
  397.     }
  398.     if (*p == '[') {
  399.     return SUCCEED;
  400.     }
  401.     while (c = *p++) {
  402.     if ( !(isalnum(c) || c == '.' || c == '-' || c == '_' ||
  403.            c == '+' || c == '=') )
  404.     {
  405.         *remainder = "illegal character in hostname";
  406.         return FAIL;
  407.     }
  408.     }
  409.     return SUCCEED;
  410. }
  411.  
  412.  
  413. /*
  414.  * build_uucp_route - convert an address into a UUCP route.
  415.  *
  416.  * Given an address using any of the addressing forms known to the
  417.  * parse_address() routine, convert that address into a pure uucp
  418.  * !-route.  The return value is always freeable with free().
  419.  *
  420.  * If there is an error, return NULL.
  421.  *
  422.  * inputs:
  423.  *    address    - the address to transform into a UUCP !-route
  424.  *    error    - on error, set this to error message, if non-NULL
  425.  *
  426.  * output:
  427.  *    transformed address, or NULL if a syntax error occured
  428.  */
  429. char *
  430. build_uucp_route(address, error)
  431.     char *address;            /* address to transform into !-route */
  432.     char **error;            /* return an error message here */
  433. {
  434.     return internal_build_uucp_route(address, error, FALSE);
  435. }
  436.  
  437. /*
  438.  * build_partial_uucp_route - convert an address into a partial UUCP route.
  439.  *
  440.  * Given an address using any of the addressing forms known to the
  441.  * parse_address routine, convert that address into a uucp !-route,
  442.  * possibly with %-forms left at the end.  The return value is always
  443.  * freeable with free().
  444.  *
  445.  * If there is an error, return NULL.
  446.  *
  447.  * inputs:
  448.  *    address    - the address to transform into a UUCP !-route
  449.  *    error    - on error, set this to error message, if non-NULL
  450.  *
  451.  * output:
  452.  *    transformed address, or NULL if a syntax error occured
  453.  */
  454. char *
  455. build_partial_uucp_route(address, error)
  456.     char *address;            /* address to transform into !-route */
  457.     char **error;            /* return an error message here */
  458. {
  459.     return internal_build_uucp_route(address, error, TRUE);
  460. }
  461.  
  462. /*
  463.  * internal_build_uucp_route - internal form for uucp-route building
  464.  *
  465.  * called from build_uucp_route and build_partial_uucp_route.  If the
  466.  * `partial' flag is TRUE then the latter style is used, otherwise a
  467.  * pure !-route is built.
  468.  */
  469. static char *
  470. internal_build_uucp_route(address, error, partial)
  471.     char *address;            /* address to transform into !-route */
  472.     char **error;            /* return an error message here */
  473.     int partial;            /* TRUE to allow %-form in route */
  474. {
  475.     struct str str;
  476.     register struct str *sp = &str;    /* dynamic string region */
  477.     int uucp_route = TRUE;        /* TRUE if already pure !-route */
  478.     char *target;            /* target returned by parse_address */
  479.     char *remainder;            /* remainder from parse_address */
  480.     char *storage;            /* malloc region for old address */
  481.  
  482.     /*
  483.      * allocate a new copy of the address so it can be examined destructively.
  484.      */
  485.     storage = remainder = xmalloc((unsigned)(strlen(address) + 1));
  486.     (void)strcpy(storage, address);
  487.  
  488.     /* initialize for copy into string region */
  489.     STR_INIT(sp);
  490.  
  491.     /* loop until we have a local form or a %-form an error occurs */
  492.     for (;;) {
  493.     int form = parse_address(remainder, &target, &remainder);
  494.  
  495.     switch (form) {
  496.  
  497.     case FAIL:            /* something went wrong, somewhere */
  498.         *error = remainder;
  499.         return NULL;
  500.  
  501.     case UUCP_ROUTE:        /* okay, this part is a !-route */
  502.         STR_CAT(sp, target);    /* add target! to route */
  503.         STR_NEXT(sp, '!');
  504.         break;
  505.  
  506.     case PCT_MAILBOX:        /* matched something%host... */
  507.         /*
  508.          * If we are building a pure uucp route, then a%b is just
  509.          * another remote form.  Otherwise, finding this form ends
  510.          * the parsing process.
  511.          */
  512.         if (!partial) {
  513.         goto remote_form;
  514.         }
  515.         /* FALL THROUGH */
  516.  
  517.     case LOCAL:            /* local form, we are done */
  518.         /* if address was already a pure !-route, return the old one */
  519.         if (uucp_route) {
  520.         /* free garbage */
  521.         free(storage);
  522.         STR_FREE(sp);
  523.         return COPY_STRING(address);
  524.         } else {
  525.         /* append final local-part */
  526.         STR_CAT(sp, remainder);
  527.         if (form == PCT_MAILBOX) {
  528.             /* %-form requires the target to be included */
  529.             STR_NEXT(sp, '%');
  530.             STR_CAT(sp, target);
  531.         }
  532.         STR_NEXT(sp, '\0');
  533.         free(storage);        /* free garbage */
  534.         STR_DONE(sp);
  535.         return sp->p;        /* return completed !-route */
  536.         }
  537.         /*NOTREACHED*/
  538.  
  539.     default:            /* not pure !-route, other form */
  540.     remote_form:
  541.         STR_CAT(sp, target);    /* add target! to route */
  542.         STR_NEXT(sp, '!');
  543.         uucp_route = FALSE;
  544.     }
  545.     }
  546. }
  547.  
  548.  
  549. /*
  550.  * address_token - scan forward one token in an address
  551.  *
  552.  * an address token is delimited by a character from the set [@!%:,]
  553.  * a token can also be a domain literal between [ and ], or
  554.  * a quoted literal between double quotes.  \ can precede a character
  555.  * to take away its special properties.
  556.  * domain literals and quoted literals and other tokens can be strung
  557.  * together into one single token if they are separated by `.'.  Otherwise
  558.  * a domain literal or quoted literal represents one token.
  559.  *
  560.  * input:
  561.  *    ap    - pointer to start of a token
  562.  *
  563.  * output:
  564.  *    the end of the input token.  Return NULL on error.
  565.  *
  566.  * called by: parse_address
  567.  */
  568. char *
  569. address_token(ap)
  570.     register char *ap;            /* address to be scanned */
  571. {
  572.     static enum state {            /* states for the state machine */
  573.     s_normal,            /* not in a literal or \ escape */
  574.     s_cquote,            /* previous char was \ */
  575.     s_quote,            /* scanning quoted literal */
  576.     s_domlit,            /* scanning domain literal */
  577.     } state;
  578.     enum state save_state;        /* previous state for \ escape */
  579.     int dot = FALSE;            /* TRUE if last char was unescaped . */
  580.  
  581.     /* setup initial state */
  582.     switch (*ap++) {
  583.     case '\0':                /* no tokens */
  584.     return NULL;            /* error */
  585.  
  586.     case '@':                /* delimiters are one token a piece */
  587.     case '!':
  588.     case '%':
  589.     case ':':
  590.     case ',':
  591.     case '>':
  592.     case '<':
  593.     return ap;            /* so return that single token */
  594.  
  595.     case '"':                /* start in a quoted literal */
  596.     state = s_quote;
  597.     break;
  598.  
  599.     case '[':                /* start in a domain literal */
  600.     state = s_domlit;
  601.     break;
  602.  
  603.     case '.':                /* start with an initial dot */
  604.     state = s_normal;
  605.     dot = TRUE;
  606.     break;
  607.  
  608.     case '\\':                /* start initially with \ escape */
  609.     save_state = s_normal;
  610.     state = s_cquote;
  611.     break;
  612.  
  613.     default:                /* otherwise begin in normal state */
  614.     state = s_normal;
  615.     break;
  616.     }
  617.  
  618.     /*
  619.      * scan until end of token
  620.      */
  621.     while (*ap) {
  622.     switch (state) {
  623.  
  624.     case s_normal:            /* scan for token delimeter */
  625.         switch (*ap) {
  626.  
  627.         case '\\':            /* \ escape, save state, then cquote */
  628.         save_state = s_normal;
  629.         state = s_cquote;
  630.         break;
  631.  
  632.         case '[':            /* domain continue if last char is . */
  633.         if (dot) {
  634.             state = s_domlit;
  635.         } else {
  636.             return ap;
  637.         }
  638.         break;
  639.  
  640.         case '"':            /* quote continue if last char is . */
  641.         if (dot) {
  642.             state = s_quote;
  643.         } else {
  644.             return ap;
  645.         }
  646.         break;
  647.  
  648.         case '@':
  649.         case '!':
  650.         case '%':
  651.         case ':':
  652.         case ',':
  653.         case '<':
  654.         case '>':
  655.         return ap;        /* found the end of a token */
  656.         }
  657.         /* dot is TRUE if this char was a dot */
  658.         dot = ('.' == *ap++);
  659.         break;
  660.  
  661.     case s_quote:            /* scan for end of a quote */
  662.         if (*ap == '\\') {
  663.         /* \ escape in quote */
  664.         ap++;
  665.         save_state = s_quote;
  666.         state = s_cquote;
  667.         } else if (*ap++ == '"') {
  668.         /* end of quote -- check for . after it */
  669.         if (*ap == '.') {
  670.             /* if exists, continue scanning */
  671.             state = s_normal;
  672.         } else {
  673.             /* otherwise we have a complete token */
  674.             return ap;
  675.         }
  676.         }
  677.         break;
  678.  
  679.     case s_domlit:            /* scan for end of domain literal */
  680.         if (*ap == '\\') {
  681.         /* \ escape in domain literal */
  682.         ap++;
  683.         save_state = s_domlit;
  684.         state = s_cquote;
  685.         } else if (*ap++ == ']') {
  686.         /* end of domain literal -- check for . after it */
  687.         if (*ap == '.') {
  688.             /* if exists, continue scanning */
  689.             state = s_normal;
  690.         } else {
  691.             /* otherwise we have a complete token */
  692.             return ap;
  693.         }
  694.         }
  695.         break;
  696.  
  697.     case s_cquote:            /* process \ escape */
  698.         ap++;            /* just skip the char */
  699.         state = save_state;        /* and return to previous state */
  700.         break;
  701.     }
  702.     }
  703.  
  704.     /*
  705.      * fell through -- error if we are not in the normal state
  706.      */
  707.     if (state != s_normal) {
  708.     return NULL;
  709.     }
  710.  
  711.     return ap;                /* all done, return the token */
  712.  
  713. }
  714.  
  715.  
  716. /*
  717.  * back_address_token - scan backward one token in an address
  718.  *
  719.  * see the rules in address_token for how to delimit an address token.
  720.  * This procedure does it going backwards.
  721.  *
  722.  * Note:  this routine is more complex than address_token, because
  723.  *      addresses are intended to be scanned forward.
  724.  *
  725.  * inputs:
  726.  *    ba    - beginning of an address (firewall)
  727.  *    ap    - pointer to character past end of token
  728.  *
  729.  * output:
  730.  *    return start of token that ap points past.  Return NULL on error.
  731.  *
  732.  * called by: parse_address
  733.  * calls: escaped
  734.  */
  735. char *
  736. back_address_token(ba, ap)
  737.     register char *ba;            /* beginning of address (firewall) */
  738.     register char *ap;            /* character past end of token */
  739. {
  740.     static enum state {            /* states for the state machine */
  741.     s_normal,            /* not in a literal */
  742.     s_quote,            /* scanning quoted literal */
  743.     s_domlit,            /* scanning domain literal */
  744.     } state;
  745.     int dot = FALSE;            /* TRUE if next char is unescaped . */
  746.     register char *p;            /* temp */
  747.  
  748.     /*
  749.      * trap no tokens
  750.      */
  751.     if (ba == ap) {
  752.     return NULL;
  753.     }
  754.  
  755.     /*
  756.      * setup initial state
  757.      */
  758.     --ap;                /* backup to end of token */
  759.     if (p = escaped(ba, ap)) {
  760.     /* if last char is escaped, we are in the normal state */
  761.     state = s_normal;
  762.     ap = p;
  763.     } else {
  764.     switch (*ap) {
  765.     case '@':            /* delimiters are one token a piece */
  766.     case '!':
  767.     case '%':
  768.     case ':':
  769.     case ',':
  770.     case '>':
  771.     case '<':
  772.         return ap;            /* so return that single token */
  773.  
  774.     case '"':            /* start in a quoted literal */
  775.         state = s_quote;
  776.         break;
  777.  
  778.     case ']':            /* start in a domain literal */
  779.         state = s_domlit;
  780.         break;
  781.  
  782.     case '.':            /* start with an initial dot */
  783.         state = s_normal;
  784.         dot = TRUE;
  785.         break;
  786.  
  787.     default:            /* otherwise begin in normal state */
  788.         state = s_normal;
  789.         break;
  790.     }
  791.     --ap;                /* this char already processed */
  792.     }
  793.  
  794.     /*
  795.      * scan until beginning of token
  796.      */
  797.     while (ap - ba >= 0) {
  798.     switch (state) {
  799.  
  800.     case s_normal:            /* scan for token delimeter */
  801.         /* trap escaped character */
  802.         if (p = escaped(ba, ap)) {
  803.         ap = p;
  804.         } else {
  805.         /* not escaped, process it */
  806.         switch (*ap) {
  807.  
  808.         case ']':        /* domain okay if next char is . */
  809.             if (dot) {
  810.             state = s_domlit;
  811.             } else {
  812.             return ap+1;
  813.             }
  814.             break;
  815.  
  816.         case '"':        /* quote okay if next char is . */
  817.             if (dot) {
  818.             state = s_quote;
  819.             } else {
  820.             return ap+1;
  821.             }
  822.             break;
  823.  
  824.         case '@':
  825.         case '!':
  826.         case '%':
  827.         case ':':
  828.         case ',':
  829.         case '>':
  830.         case '<':
  831.             return ap+1;    /* found the end of a token */
  832.         }
  833.         /* dot is TRUE if this char was a dot */
  834.         dot = ('.' == *ap--);
  835.         }
  836.         break;
  837.  
  838.     case s_quote:            /* scan for end of a quote */
  839.         if (p = escaped(ba, ap)) {
  840.         /* trap \ escape */
  841.         ap = p;
  842.         } else if (*ap-- == '"') {
  843.         /* end of quote -- check for . before it */
  844.         if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
  845.             /* if exists, continue scanning */
  846.             state = s_normal;
  847.         } else {
  848.             /* otherwise we have a complete token */
  849.             return ap+1;
  850.         }
  851.         }
  852.         break;
  853.  
  854.     case s_domlit:            /* scan for end of domain literal */
  855.         if (p = escaped(ba, ap)) {
  856.         /* trap \ escape */
  857.         ap = p;
  858.         } else if (*ap-- == '[') {
  859.         /* end of domain literal -- check for . before it */
  860.         if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
  861.             /* if exists, continue scanning */
  862.             state = s_normal;
  863.         } else {
  864.             /* otherwise we have a complete token */
  865.             return ap+1;
  866.         }
  867.         }
  868.         break;
  869.     }
  870.     }
  871.  
  872.     /*
  873.      * fell through -- error if we are not in the normal state
  874.      */
  875.     if (state != s_normal) {
  876.     return NULL;
  877.     }
  878.  
  879.     return ap+1;            /* all done, return the token */
  880. }
  881.  
  882. /*
  883.  * escaped - determine if a character is \ escaped, scanning backward
  884.  *
  885.  * given the beginning of a string and a character positition within
  886.  * it, determine if that character is \ escaped or not, tracing through
  887.  * multiple \ chars if necessary.  Basically, if the character position
  888.  * is preceded by an odd number of \ chars, the current character is
  889.  * \ escaped.
  890.  *
  891.  * inputs:
  892.  *    ba    - beginning of string
  893.  *    ap    - character position in string
  894.  *
  895.  * output:
  896.  *    beginning of set of \ chars previous to ap, or NULL if the
  897.  *    character at ap is not backslash escaped.
  898.  *
  899.  * called by: back_address_token
  900.  */
  901. static char *
  902. escaped(ba, ap)
  903.     register char *ba;            /* beginning of string */
  904.     register char *ap;            /* character position in string */
  905. {
  906.     register unsigned i = 0;        /* count of \ characters */
  907.  
  908.     /*
  909.      * count the number of preceding \ characters, but don't go past
  910.      * the beginning of the string.
  911.      */
  912.     --ap;
  913.     while (ap - ba >= 0 && *ap == '\\') {
  914.     i++; --ap;
  915.     }
  916.  
  917.     /* if odd number of \ chars, then backslash escaped */
  918.     return (i%2==1)? ap: NULL;
  919. }
  920.  
  921.  
  922. /*
  923.  * alloc_addr - allocate a struct addr
  924.  *
  925.  * NOTE: the caller must setup the addr fields correctly.  This routine
  926.  *     marks certain fields with improper values, which unless changed,
  927.  *     will results in other routines doing a panic().
  928.  */
  929. struct addr *
  930. alloc_addr()
  931. {
  932.     register struct addr *addr;        /* our new address */
  933.  
  934.     /* grab it */
  935.     addr = (struct addr *)xmalloc(sizeof(struct addr));
  936.  
  937.     /* preset the proper values */
  938.     addr->succ = NULL;
  939.     addr->flags = 0;
  940.     addr->parent = NULL;
  941.     addr->true_addr = NULL;
  942.     addr->in_addr = NULL;
  943.     addr->target = NULL;
  944.     addr->remainder = NULL;
  945.     addr->work_addr = NULL;
  946.     addr->match_count = -1;
  947.     addr->route = NULL;
  948.     addr->router = NULL;
  949.     addr->director = NULL;
  950.     addr->next_host = NULL;
  951.     addr->next_addr = NULL;
  952.     addr->transport = NULL;
  953.     addr->home = NULL;
  954.     addr->uid = BOGUS_USER;        /* the identity is not known yet */
  955.     addr->gid = BOGUS_GROUP;        /* the identity is not known yet */
  956.     addr->error = NULL;
  957.  
  958.     return addr;
  959. }
  960.  
  961. /*
  962.  * insert_addr_list - insert a list of addrs into another list
  963.  *
  964.  * insert each addr in an input list at the beginning of an output list.
  965.  * In the process or in some addr flags and (possibly) set next_addr
  966.  * to an error message.
  967.  */
  968. void
  969. insert_addr_list(in, out, error)
  970.     register struct addr *in;        /* input list */
  971.     register struct addr **out;        /* output list */
  972.     register struct error *error;    /* error structure (if non-NULL) */
  973. {
  974.     struct addr *next;
  975.  
  976.     /* loop over all of the input addrs */
  977.     for (; in; in = next) {
  978.     next = in->succ;
  979.  
  980.     if (error) {
  981.         in->error = error;        /* set the error message, if given */
  982.     }
  983.     in->succ = *out;
  984.     *out = in;
  985.     }
  986. }
  987.  
  988. /*
  989.  * addr_sort - sort an input list of addrs and return the new sorted list
  990.  *
  991.  * calling sequence is:
  992.  *    sorted_list = addr_sort(input_list, OFFSET(addr, tag_name)
  993.  *
  994.  * where tag_name is the (char *) element name in the addr structure to
  995.  * sort on.
  996.  */
  997. static int sort_offset;            /* pass offset to compare function */
  998. struct addr *
  999. addr_sort(in, offset)
  1000.     struct addr *in;
  1001.     int offset;
  1002. {
  1003.     struct addr **addrv;        /* array of addresses */
  1004.     register int addrc;            /* count of addresses */
  1005.     register struct addr **addrp;    /* temp addr pointer */
  1006.     register struct addr *a;        /* address list or current address */
  1007.  
  1008.     /* pass offset value to addrcmp() by setting file local variable */
  1009.     sort_offset = offset;
  1010.  
  1011.     /* count the input addresses */
  1012.     addrc = 0;
  1013.     for (a = in; a; a = a->succ) {
  1014.     addrc++;
  1015.     }
  1016.  
  1017.     /* allocate space for an array for that many pointers */
  1018.     addrv = (struct addr **)xmalloc(addrc * sizeof(struct addr));
  1019.  
  1020.     /* build the array from the input list */
  1021.     for (addrp = addrv, a = in; a; a = a->succ) {
  1022.     *addrp++ = a;
  1023.     }
  1024.  
  1025.     /* sort the array */
  1026.     qsort((char *)addrv, addrc, sizeof(*addrv), addrcmp);
  1027.  
  1028.     /*
  1029.      * turn the sorted array into a sorted list
  1030.      * Start from the end of the array so the generated list will start
  1031.      * from the beginning.
  1032.      */
  1033.     for (addrp = addrv + addrc, a = NULL; addrc > 0; --addrc) {
  1034.     (*--addrp)->succ = a;
  1035.     a = *addrp;
  1036.     }
  1037.  
  1038.     return a;
  1039. }
  1040.  
  1041. /*
  1042.  * addrcmp - compare two addr structures based on a field at sort_offset.
  1043.  */
  1044. static int
  1045. addrcmp(a, b)
  1046.     char **a;
  1047.     char **b;
  1048. {
  1049.     return strcmp(*(char **)(*a + sort_offset), *(char **)(*b + sort_offset));
  1050. }
  1051.  
  1052. /*
  1053.  * note_error - create an error structure for inclusion in an addr structure
  1054.  */
  1055. struct error *
  1056. note_error(info, message)
  1057.     long info;
  1058.     char *message;
  1059. {
  1060.     struct error *ret = (struct error *)xmalloc(sizeof(struct error));
  1061.  
  1062.     ret->info = info;
  1063.     ret->message = message;
  1064.  
  1065.     return ret;
  1066. }
  1067.  
  1068.  
  1069. #ifdef STANDALONE
  1070.  
  1071. #include <stdarg.h>
  1072.  
  1073. int return_to_sender = FALSE;
  1074. int exitvalue = 0;
  1075.  
  1076. #ifdef DEBUG_LEVEL
  1077.  int debug = DEBUG_LEVEL;
  1078. #else    /* DEBUG_LEVEL */
  1079.  int debug = 0;
  1080. #endif    /* DEBUG_LEVEL */
  1081.  
  1082. /*
  1083.  * test the above functions by calling parse_address for each
  1084.  * argument given to the program.
  1085.  */
  1086. void
  1087. main(argc, argv)
  1088.     int argc;                /* count of arguments */
  1089.     char **argv;            /* vector of arguments */
  1090. {
  1091.     char *s;                /* temp string */
  1092.     char *addr;                /* preparsed address */
  1093.     char *error;            /* error message */
  1094.     int form;                /* form from parse_address */
  1095.     char *target;            /* target returned by parse_address */
  1096.     char *remainder;            /* remainder from parse_address */
  1097.  
  1098.     /*
  1099.      * if first argument is a number, change the debug level
  1100.      */
  1101.     if (isdigit(argv[1][0])) {
  1102.     debug = atoi(*++argv);
  1103.     argc--;
  1104.     }
  1105.  
  1106.     /*
  1107.      * loop over all arguments or read from standard input if none
  1108.      */
  1109.     if (argc > 1) {
  1110.     while (*++argv) {
  1111.         (void)fprintf(stderr, "input:  %s\n", *argv);
  1112.  
  1113.         /* preparse the address to get rid of mutant forms */
  1114.         addr = preparse_address(*argv, &error);
  1115.         if (addr) {
  1116.         (void)fprintf(stderr, "preparse_address: %s\n", addr);
  1117.         } else {
  1118.         (void)fprintf(stderr, "preparse_address: %s\n", error);
  1119.         break;
  1120.         }
  1121.  
  1122.         /* see what build_uucp_route yields */
  1123.         s = build_uucp_route(addr, &error);
  1124.         if (s) {
  1125.         (void)fprintf(stderr, "build_uucp_route: %s\n", s);
  1126.         } else {
  1127.         (void)fprintf(stderr, "build_uucp_route: %s\n", error);
  1128.         }
  1129.  
  1130.         /* see what parse_address yields */
  1131.         form = parse_address(addr, &target, &remainder);
  1132.         if (form == LOCAL) {
  1133.         (void)printf("LOCAL %s\n", remainder);
  1134.         } else if (form == FAIL) {
  1135.         (void)fprintf(stderr, "parse_address: %s\n", remainder);
  1136.         } else {
  1137.         (void)printf("REMOTE %s@%s\n", remainder, target);
  1138.         }
  1139.     }
  1140.     } else {
  1141.     char line[4096];
  1142.  
  1143.     while (gets(line) != NULL) {
  1144.         (void)fprintf(stderr, "input:  %s\n", line);
  1145.  
  1146.         /* preparse the address to get rid of mutant forms */
  1147.         addr = preparse_address(line, &error);
  1148.         if (addr) {
  1149.         (void)fprintf(stderr, "preparse_address: %s\n", addr);
  1150.         } else {
  1151.         (void)fprintf(stderr, "preparse_address: %s\n", error);
  1152.         break;
  1153.         }
  1154.  
  1155.         /* see what build_uucp_route yields */
  1156.         s = build_uucp_route(addr, &error);
  1157.         if (s) {
  1158.         (void)fprintf(stderr, "build_uucp_route: %s\n", s);
  1159.         } else {
  1160.         (void)fprintf(stderr, "build_uucp_route: %s\n", error);
  1161.         }
  1162.  
  1163.         /* see what parse_address yields */
  1164.         form = parse_address(addr, &target, &remainder);
  1165.         if (form == LOCAL) {
  1166.         (void)printf("LOCAL %s\n", remainder);
  1167.         } else if (form == FAIL) {
  1168.         (void)fprintf(stderr, "parse_address: %s\n", remainder);
  1169.         } else {
  1170.         (void)printf("REMOTE %s@%s\n", remainder, target);
  1171.         }
  1172.     }
  1173.     }
  1174.  
  1175.     exit(exitvalue);
  1176. }
  1177. #endif /* STANDALONE */
  1178. void
  1179. str_cat(sp, s)
  1180.     register struct str *sp;
  1181.     char *s;
  1182. {
  1183.     register unsigned l;        /* maximum chars needed */
  1184.  
  1185.     l = (unsigned)strlen(s);
  1186.     if (l + sp->i > sp->a) {
  1187.     /*
  1188.      * need to expand the region:
  1189.      * bump up to a sufficiently large region which is a multiple
  1190.      * of STR_BUMP (except for a pointer).  Allow at least 10 free
  1191.      * chars in the region, for future expansion.
  1192.      */
  1193.     sp->a = (sp->i + l + STR_BUMP + 10) & (~(STR_BUMP-1)) - sizeof(long);
  1194.     sp->p = xrealloc(sp->p, sp->a);
  1195.     }
  1196.     /* copy string to the end of the region */
  1197.     (void) memcpy(sp->p + sp->i, s, l);
  1198.     sp->i += l;
  1199. }
  1200.