home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / mush6.4 / part15 / addrs.c
C/C++ Source or Header  |  1989-03-12  |  32KB  |  1,084 lines

  1. /* addrs.c -- copyright (c) Dan Heller 1/25/1989 */
  2.  
  3. #include "mush.h"
  4.  
  5. /*
  6.  * Check to see if all addressees in list1 is in list2.
  7.  * The lists must be as clean as the driven snow (no comments, aliases
  8.  * must have been expanded, all are separated by commas or whitespace.
  9.  *
  10.  * "user" matches "user" and "user@localhost"
  11.  * "*user" matches "user" at any address whatsoever."
  12.  * !host matches any user destined for the specified host.
  13.  * !some!path is the same, but can be more specifiec in the path.
  14.  * @dom.ain can match any user destined for any host within the domain.
  15.  *      @berkeley.edu would match: dheller@cory.berkeley.edu
  16.  */
  17. compare_addrs(list1, list2, ret_buf)
  18. char *list1, *list2, ret_buf[];
  19. {
  20.     register char    *p;
  21.     char        **addrv, **listv, buf[256]; /* addrs aren't long */
  22.     int            addrc, listc, a, l, h, ret_val;
  23.  
  24.     /* autosign2 list contains non-comment addresses */
  25.     listv = mk_argv(list1, &listc, FALSE);
  26.     addrv = mk_argv(list2, &addrc, FALSE);
  27.  
  28.     /* loop thru both lists and convert addresses to !-format
  29.      * then remove ourhost names so "user" matches "user!local"
  30.      * also remove possible trailing commas (from list).
  31.      */
  32.     for (a = 0; a < addrc; a++) {
  33.     if (a != addrc-1 && (p = index(addrv[a], ',')) && !p[1])
  34.         *p = 0;
  35.     if (addrv[a][0] == '!' || addrv[a][0] == '@')
  36.         continue;
  37.     (void) bang_form(buf, addrv[a]);
  38.     if (strcmp(addrv[a], buf)) /* if they differ... */
  39.         (void) strcpy(addrv[a], buf); /* save new version */
  40.     }
  41.     for (l = 0; l < listc; l++) {
  42.     if (l != listc-1 && (p = index(listv[l], ',')) && !p[1])
  43.         *p = 0;
  44.     if (listv[l][0] == '!' || listv[l][0] == '@')
  45.         continue;
  46.     (void) bang_form(buf, listv[l]);
  47.     if (strcmp(listv[l], buf)) /* if they differ... */
  48.         (void) strdup(listv[l], buf); /* save new version */
  49.     }
  50.  
  51.     Debug("\nlist1 = "), print_argv(listv);
  52.     Debug("list2 = "), print_argv(addrv), putchar('\n');
  53.  
  54.     /* loop thru each list comparing each element with the
  55.      * other, if necessary.
  56.      */
  57.     for (l = 0; l < listc; l++) {
  58.     ret_val = 0;
  59.     /* check if local recipient with was specified. */
  60.     if (!(p = rindex(listv[l], '!')))
  61.         for (a = 0; a < addrc; a++) {
  62.         /* we have a local user so far.  If addrv[] is
  63.          * not remote, then strcmp() immediately.
  64.          * Note that "!" with no host indicates *all*
  65.          * local users!!!
  66.          */
  67.         if (addrv[a][0] != '!') {
  68.            if (!lcase_strncmp(addrv[a], listv[l], -1) || !addrv[a][1])
  69.             ret_val = 1;
  70.         } else if (addrv[a][0] == '*') {
  71.             /* "*user" == "user" or "*" == login */
  72.             if (!addrv[a][1] && !lcase_strncmp(listv[l], login) ||
  73.             !lcase_strncmp(listv[l], addrv[a]+1, -1))
  74.             ret_val = 1;
  75.         } else for (h = 0; ourname && ourname[h]; h++)
  76.             if (!lcase_strncmp(addrv[a]+1,
  77.             ourname[h], -1)) {
  78.             ret_val = 1;
  79.             break;
  80.             }
  81.         if (ret_val)
  82.             break;
  83.         }
  84.     /* else this is a remote user */
  85.     else {
  86.         /* check all the addresses for @dom.ain stuff or
  87.          * !path!name type stuff only.
  88.          */
  89.         /* first back up p to the previous '!' */
  90.         char *start, *user = p + 1;
  91.         while (p-1 >= listv[l] && *--p != '!')
  92.         ;
  93.         start = p; /* Where to start for _domain_ addrs */
  94.         for (a = 0; a < addrc; a++) {
  95.         int len;
  96.         char *path;
  97.  
  98.         /* first check the cases of address unmodified by @ and !
  99.          * or check to see if  *user  is specified.
  100.          */ 
  101.         if (addrv[a][0] != '@' && addrv[a][0] != '!') {
  102.             if (addrv[a][0] == '*') {
  103.             /* we saved the username at "user" declaration. */
  104.             /* if "*" is by itself, check against user's login */
  105.             if (!addrv[a][1] && !lcase_strncmp(user, login, -1) ||
  106.                 addrv[a][1] && !lcase_strncmp(user, addrv[a]+1,-1)){
  107.                 ret_val = 1;
  108.                 break;
  109.             }
  110.             } else if (!lcase_strncmp(addrv[a], listv[l], -1)) {
  111.             ret_val = 1;
  112.             break;
  113.             }
  114.             continue;
  115.         }
  116.         path = addrv[a]+1;
  117.         while (addrv[a][1] == '@' && *path == '.')
  118.             path++;
  119.         if ((len = strlen(path)) == 0)
  120.             continue; /* localhost stuff only -- can't match */
  121.         /* first check against specified domains */
  122.         if (addrv[a][0] == '@') {
  123.             for (p = start; p; (p = index(p, '.')) && ++p)
  124.             if (!lcase_strncmp(p, path, len) &&
  125.                 (p[len] == '.' || p[len] == 0 || p[len] == '!')) {
  126.                 ret_val = 1;
  127.                 break;
  128.             }
  129.         } else if (addrv[a][0] == '!') {
  130.             /* for !path style, start at head of addr */
  131.             for (p = listv[l]; p; (p = index(p, '!')) && ++p)
  132.             if (!lcase_strncmp(p, path, len) &&
  133.                 (p[len] == '!' || p[len] == 0)) {
  134.                 ret_val = 1;
  135.                 break;
  136.             }
  137.         }
  138.         /* If address is in autosign2, goto next addr */
  139.         if (ret_val)
  140.             break;
  141.         }
  142.     }
  143.     if (!ret_val) {
  144.         /* this address isn't in autosign2 list */
  145.         if (ret_buf)
  146.         (void) strcpy(ret_buf, listv[l]);
  147.         break;
  148.     }
  149.     }
  150.     free_vec(listv);
  151.     free_vec(addrv);
  152.  
  153.     return ret_val;
  154. }
  155.  
  156. /*
  157.  * Parser for stupidly-formed RFC822 addresses.  It has been tested on
  158.  * several bizzare cases as well as the normal stuff and uucp paths.  It
  159.  * takes a string which is a bunch of addresses and unscrambles the first
  160.  * one in the string.  It returns a pointer to the first char past what it
  161.  * unscrambled and copies the unscrambled address to its second argument.
  162.  * 
  163.  * It does NOT deal with trailing (comment) strings --
  164.  *         <whoever@somewhere> (This is a comment)
  165.  *                            ^unscramble_addr return points here
  166.  * 
  167.  * It also does not deal well with malformed <addresses> --
  168.  *         <whoever@somewhere,nowhere>
  169.  *                           ^unscramble_addr return points here
  170.  * 
  171.  * In each of the above cases, the string "whoever@somewhere" is copied
  172.  * to the second argument.
  173.  * 
  174.  * Nothing is done to un-<>ed route-less RFC822/976 addresses, nor to
  175.  * uucp paths, nor to mixed-mode addresses not containing a route.
  176.  * Hopelessly scrambled addresses are not handled brilliantly --
  177.  *     @some.dumb.place,@any.other.place:sys2!user%sys3@sys1
  178.  * parses to
  179.  *     sys2!user%sys3@sys1
  180.  * i.e., the route is simply dropped.
  181.  */
  182. char *
  183. unscramble_addr(addr, naddr)
  184. char *addr;
  185. char *naddr;
  186. {
  187.     char *i, *r, *at;
  188.     char s[BUFSIZ], t[BUFSIZ];
  189.     int anglebrace = 0;
  190.  
  191.     /* Make a copy of the address so we can mangle it freely. */
  192.     if (addr && *addr) {
  193.     /* Skip any leading whitespace. */
  194.     for (i = addr; *i && (r = any(i, " \t")) == i;)
  195.         if (r) i = r + 1;
  196.     if (*i == '\0')
  197.         return NULL;
  198.     /* Skip any leading double-quoted comment. */
  199.     if (*i == '"') {
  200.         if ((i = index(i + 1, '"')) && (*i == '\0' || *(++i) == '\0'))
  201.             return NULL;
  202.     }
  203.     /* Skip any more whitespace. */
  204.     for (; *i && (r = any(i, " \t")) == i;)
  205.         if (r) i = r + 1;
  206.     if (*i == '\0')
  207.         return NULL;
  208.     /* Check for angle braces around the address. */
  209.     if (*i == '<') {
  210.         if (*(++i) == '\0')
  211.         return NULL;
  212.         ++anglebrace;
  213.     }
  214.     /*
  215.      * Look for a route.  A route is a comma-separated set of @-tagged
  216.      *  domains terminated by a colon.  Later versions might try to use
  217.      *  the route, but for now it confuses too many mailers.
  218.      */
  219.     if ((*i == '@') && (r = any(i, " \t:"))) {
  220.         if (*r != ':')
  221.         return NULL;
  222.         if (*(r + 1) == '\0')
  223.         return NULL;
  224.         /*
  225.          * Back up to the rightmost @-tagged domain
  226.          *  (see note below about unwinding)
  227.          */
  228.         *r = '\0';
  229.         i = rindex(i, '@');
  230.         *r = ':';
  231.     }
  232.     /* Remember how much we've skipped, and copy the rest. */
  233.     at = i;
  234.     (void) strcpy(t,i);
  235.     /* Strip from a trailing angle brace, if present. */
  236.     if (anglebrace) {
  237.         if (r = any(t, "> \t")) {
  238.         if (r == t || *r != '>')
  239.             return NULL;
  240.         else
  241.             *r = '\0';
  242.         --anglebrace;
  243.         } else
  244.         return NULL;
  245.     }
  246.     if (t[0] == '@') {
  247.         /* Chop off any invalid stuff after the address. */
  248.         if (r = any(index(t, ':'), " \t,(<"))
  249.         *r = '\0';
  250.     }
  251.     } else
  252.     return NULL;
  253.     /* Remember where we are so we can return it. */
  254.     at += strlen(t) + 1;
  255.     /*
  256.      * Unscramble the route, if present.
  257.      *  NOTE:  We assume that a route is present in only two cases:
  258.      *   1) addr was taken from the "From " line of a stupid mailer
  259.      *   2) addr was a well-formed, <> enclosed RFC822 address
  260.      */
  261.     if (t[0] == '@') {
  262.     if (r = index(t, ':'))
  263.         r++;
  264.     else
  265.         return NULL;
  266.     /* Delete the route if extraneous, otherwise unwind it. */
  267.     if (i = index(r, '@'))
  268.         (void) strcpy(s, r);
  269.     else {
  270.         /*
  271.          * NOTE:  Unwinding currently uses only the rightmost domain
  272.          *  in the route.  This will break for mailers that need the
  273.          *  entire route.  Complete unwinding would require the use
  274.          *  of % characters, which are avoided for other reasons.
  275.          */
  276.         (void) strcpy(s, r);
  277.         *(--r) = '\0';
  278.         (void) strcat(s, t);
  279.     }
  280.     } else
  281.     (void) strcpy(s, t);
  282.     /*
  283.      * Ok, now the address should be in the form user@domain and
  284.      *  is held in buffer s (t[] is not copied directly to naddr
  285.      *  to allow future additional processing to be added here).
  286.      */
  287.     if (debug > 1) /* Don't dump this on trivial debugging */
  288.     print("Converting \"%s\" to \"%s\"\n", addr, s);
  289.     (void) strcpy(naddr, s);
  290.     return at;
  291. }
  292.  
  293. /*
  294.  * Convert RFC822 or mixed addresses to RFC976 `!' form,
  295.  *  copying the new address to d.  The source address is
  296.  *  translated according to RFC822 rules.
  297.  * Return a pointer to the end (nul terminus) of d.
  298.  */
  299. char *
  300. bang_form (d, s)
  301. char *d, *s;
  302. {
  303.     char *r, *t, *ab = NULL;
  304.  
  305.     *d = '\0';
  306.     /* If nothing to do, quit now */
  307.     if (!s || !*s) {
  308.     return d;
  309.     }
  310.     /* Avoid any angle braces */
  311.     if (*s == '<') {
  312.     if (ab = index(s + 1, '>'))
  313.         s++, *ab = '\0';
  314.     else
  315.         return NULL;
  316.     }
  317.     /*
  318.      * Look backwards for the first `@'; this gives us the
  319.      * primary domain of the RFC822 address
  320.      */
  321.     if ((t = rindex(s, '@')) && t != s) {
  322.     /* Copy the RFC822 domain as the UUCP head */
  323.     d += Strcpy(d, t + 1);
  324.     *d++ = '!';
  325.     *t = '\0';
  326.     r = bang_form(d, s);
  327.     *t = '@';
  328.     } else if (*s == '@') {
  329.     /* An RFC-822 "@domain1,@domain2:" routing */
  330.     if (t = any(++s, ",:")) {
  331.         char c = *t;
  332.         *t = '\0';
  333.         d += Strcpy(d, s);
  334.         *d++ = '!';
  335.         *t++ = c;
  336.         r = bang_form(d, t);
  337.     } else
  338.         r = NULL;
  339.     } else if (t = index(s, '!')) {
  340.     /* A normal UUCP path */
  341.     *t = '\0';
  342.     d += Strcpy(d, s);
  343.     *t++ = *d++ = '!';
  344.     r = bang_form(d, t);
  345.     } else if (t = rindex(s, '%')) {
  346.     /* An imbedded `%' -- treat as low-priority `@' */
  347.     *t = '@';
  348.     r = bang_form(d, s);
  349.     *t = '%';
  350.     } else
  351.     r = d + Strcpy(d, s);  /* No `@', `!', or `%' */
  352.     if (ab)
  353.     *ab = '>';
  354.     return r;
  355. }
  356.  
  357. /*
  358.  * Route addresses according to certain criteria.  This function is really
  359.  * just a front end for improve_uucp_paths() which does routing (differently).
  360.  * If "route" is null, this routine is being called incorrectly.
  361.  * If route is an address, just call improve_uucp_paths() and return.
  362.  * If route is the null string, then route all addresses via the sender's
  363.  * which is the first name/address on the To: list. If he's on a remote
  364.  * machine, chances are that the addresses of everyone else he mailed to
  365.  * are addresses from his machine.  Reconstruct those addresses to route
  366.  * thru the senders machine first.
  367.  */
  368. route_addresses(to, cc, route_path)
  369. char *to, *cc, *route_path;
  370. {
  371.     char pre_path[256], addr[256];
  372.     register char *next, *p;
  373.     int pre_len = 0;
  374.  
  375.     if (!route_path)
  376.     return;
  377.     if (*route_path) {
  378.     improve_uucp_paths(to, HDRSIZ, route_path);
  379.     improve_uucp_paths(cc, HDRSIZ, route_path);
  380.     return;
  381.     }
  382.  
  383.     pre_path[0] = 0;
  384.     /* Get the address of the sender (which is always listed first) */
  385.     if (!(next = get_name_n_addr(to, NULL, addr)))
  386.     return;
  387.     /* check to see if there is only one addr on To: line and no Cc: header */
  388.     if (!*next && (!cc || !*cc))
  389.     return;
  390.  
  391.     /* fix up the sender's address; improve_uucp_paths to optimize pre_path */
  392.     improve_uucp_paths(addr, sizeof addr, NULL);
  393.  
  394.     if (p = rindex(addr, '!')) {
  395.     *p = 0;
  396.     pre_len = Strcpy(pre_path, addr); /* the uucp route he used */
  397.     Debug("Routing thru \"%s\"\n", pre_path);
  398.     }
  399.  
  400.     while (*next == ',' || isspace(*next))
  401.      next++;
  402.     improve_uucp_paths(next, HDRSIZ - (int)(next - to), pre_path);
  403.     improve_uucp_paths(cc, HDRSIZ, pre_path);
  404. }
  405.  
  406. /*
  407.  * pass a string describing header like, "Subject: ", current value, and
  408.  * whether or not to prompt for it or to just post the information.
  409.  * If do_prompt is true, "type in" the current value so user can either
  410.  * modify it, erase it, or add to it.
  411.  */
  412. char *
  413. set_header(str, curstr, do_prompt)
  414. register char *str, *curstr;
  415. {
  416.     static char       buf[HDRSIZ];
  417.     int        offset = 0;
  418.     register char  *p = curstr;
  419.  
  420.     if (!str)
  421.     str = "";
  422.  
  423.     buf[0] = 0;
  424.     wprint(str);
  425.     fflush(stdout);         /* force str curstr */
  426.     if (do_prompt) {
  427.     if (curstr)
  428. #ifdef SUNTOOL
  429.         if (istool)
  430.         for (p = curstr; *p; p++)
  431.             rite(*p); /* mimics typing for the tool */
  432.         else
  433. #endif /* SUNTOOL */
  434.         if (isoff(glob_flags, ECHO_FLAG)) {
  435.         Ungetstr(curstr);
  436.         } else
  437. #ifdef TIOCSTI
  438.         for (p = curstr; *p; p++)
  439.             if (ioctl(0, TIOCSTI, p) == -1) {
  440.             error("ioctl: TIOCSTI");
  441.             wprint("You must retype the entire line.\n%s", str);
  442.             break;
  443.             }
  444. #else /* !TIOCSTI */
  445.         wprint("WARNING: -e flag! Type the line over.\n%s", str);
  446. #endif /* TIOCSTI */
  447.  
  448.     if (istool)
  449.         return NULL;
  450.     /* simulate the fact that we're getting input for the letter even tho
  451.      * we may not be.  set_header is called before IS_GETTING is true,
  452.      * but if we set it to true temporarily, then signals will return to
  453.      * the right place (stop/continue).
  454.      */
  455.     {
  456.         u_long getting = ison(glob_flags, IS_GETTING);
  457.         int wrapping = wrapcolumn;
  458.         /* Funky trick here.  If the prompt string is empty,
  459.          * assume that we are allowed to do line wrap;
  460.          * otherwise, temporarily disable line wrap
  461.          */
  462.         if (*str)
  463.         wrapcolumn = 0;
  464.         if (!getting)
  465.         turnon(glob_flags, IS_GETTING);
  466.         if (Getstr(buf, sizeof(buf), offset) == -1) {
  467.         putchar('\n');
  468.         buf[0] = 0;
  469.         }
  470.         if (!getting)
  471.         turnoff(glob_flags, IS_GETTING);
  472.         wrapcolumn = wrapping;
  473.     }
  474.     } else
  475.     puts(strcpy(buf, curstr));
  476.     if (debug > 1)
  477.     print("returning (%s) from set_header\n", buf);
  478.     return buf;
  479. }
  480.  
  481. /*
  482.  * improve uucp paths by looking at the name of each host listed in the
  483.  * path given.
  484.  *    sun!island!pixar!island!argv
  485.  * It's a legal address, but redundant. Also, if we know we talk to particular
  486.  * hosts via uucp, then we can just start with that host and disregard the path
  487.  * preceding it.  So, first get the known hosts and save them. Then start
  488.  * at the end of the original path (at the last ! found), and move backwards
  489.  * saving each hostname.  If we get to a host that we know about, stop there
  490.  * and use that address.  If we get to a host we've already seen, then
  491.  * delete it and all the hosts since then until the first occurrence of that
  492.  * hostname.  When we get to the beginning, the address will be complete.
  493.  * The route_path is prepended to each address to check make sure this path
  494.  * is used if no known_hosts precede it in that address.
  495.  *
  496.  * Return all results into the original buffer passed to us.  If route_path
  497.  * adds to the length of all the paths, then the original buffer could be
  498.  * overwritten.  someone should check for this!
  499.  */
  500. improve_uucp_paths(original, size, route_path)
  501. char *original, *route_path;
  502. {
  503.     char       name[256], addr[256], buf[2 * HDRSIZ], *end;
  504.     char      *hostnames[32], tmp[sizeof addr];
  505.     register char *p, *p2, *recipient, *start = original, *b = buf;
  506.     int           saved_hosts, i;
  507.  
  508.     if (!original || !*original)
  509.     return;
  510.  
  511.     while (end = get_name_n_addr(start, name, tmp)) {
  512.     /* first copy the route path, then the rest of the address. */
  513.     p = addr;
  514.     if (route_path && *route_path) {
  515.         p += Strcpy(addr, route_path);
  516.         *p++ = '!';
  517.     }
  518.     (void) bang_form(p, tmp);
  519.     saved_hosts = 0;
  520.     if (p2 = rindex(addr, '!')) {
  521.         recipient = p2+1;
  522.         /* save the uucp-style address *without* route_path in tmp */
  523.         (void) strcpy(tmp, p);
  524.         for (p = p2; p > addr; p--) {
  525.         /* null the '!' separating the rest of the path from the part
  526.          * of the path preceding it and move p back to the previous
  527.          * '!' (or beginning to addr) for hostname to point to.
  528.          */
  529.         for (*p-- = 0; p > addr && *p != '!'; p--)
  530.             ;
  531.         /* if p is not at the addr, move it forward past the '!' */
  532.         if (p != addr)
  533.             ++p; /* now points to a null terminated hostname */
  534.         /* if host is ourselves, ignore this and preceding hosts */
  535.         for (i = 0; ourname && ourname[i]; i++)
  536.             if (!lcase_strncmp(p, ourname[i], -1))
  537.             break;
  538.         if (ourname && ourname[i])
  539.             break;
  540.         /* check already saved hostnames. If host is one of them,
  541.          * delete remaining hostnames since there is a redundant path.
  542.          */
  543.         for (i = 0; i < saved_hosts; i++)
  544.             if (!lcase_strncmp(hostnames[i], p, -1))
  545.                 saved_hosts = i;
  546.  
  547.         hostnames[saved_hosts++] = p;
  548.         if (p == addr)
  549.             break;
  550.         /* If we know that we call this host, break */
  551.         for (i = 0; known_hosts && known_hosts[i]; i++)
  552.             if (!lcase_strncmp(p, known_hosts[i], -1))
  553.             break;
  554.         }
  555.         /* temporary holder for where we are in buffer (save address) */
  556.         p2 = b;
  557.         while (saved_hosts-- > 0) {
  558.         b += Strcpy(b, hostnames[saved_hosts]);
  559.         *b++ = '!';
  560.         }
  561.         b += Strcpy(b, recipient);
  562.         if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
  563.         b = p2; /* reset offset in buf (b) to where we were (p2) */
  564.         goto unmodified;
  565.         }
  566.         if (*name)
  567.         b += strlen(sprintf(b, " (%s)", name));
  568.     } else {
  569.         char c;
  570. unmodified:
  571.         c = *end;
  572.         *end = 0;
  573.         b += Strcpy(b, start); /* copy the entire address with comments */
  574.         *end = c;
  575.     }
  576.     if (b - buf > size) {
  577.         print("Warning: address list truncated!\n");
  578.         /* Use a very poor heuristic to find the last complete address */
  579.         for (b = buf+size - 1; *b != ','; b--)
  580.         ;
  581.         print("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
  582.         while (isspace(*b) || *b == ',')
  583.         b--;
  584.         break;
  585.     }
  586.     for (start = end; *start == ',' || isspace(*start); start++)
  587.         ;
  588.     if (!*start)
  589.         break;
  590.     *b++ = ',', *b++ = ' ', *b = '\0';
  591.     }
  592.     (void) strcpy(original, buf);
  593. }
  594.  
  595. /*
  596.  * rm_cmts_in_addr() removes the comment lines in addresses that result from
  597.  * sendmail or other mailers which append the user's "real name" on the
  598.  * from lines.  See get_name_n_addr().
  599.  */
  600. rm_cmts_in_addr(str)
  601. register char *str;
  602. {
  603.     char addr[BUFSIZ], buf[HDRSIZ], *start = str;
  604.     register char *b = buf;
  605.  
  606.     *b = 0;
  607.     do  {
  608.     if (!(str = get_name_n_addr(str, NULL, addr)))
  609.         break;
  610.     b += Strcpy(b, addr);
  611.     while (*str == ',' || isspace(*str))
  612.         str++;
  613.     if (*str)
  614.         *b++ = ',', *b++ = ' ', *b = '\0';
  615.     } while (*str);
  616.     for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  617.     *b = 0;
  618.     (void) strcpy(start, buf);
  619. }
  620.  
  621. /*
  622.  * take_me_off() is intended to search for the user's login name in an
  623.  * address string and remove it.  If "metoo" is set, return without change.
  624.  * determine which addresses are the "user'"'s addresses by comparing them
  625.  * against the host/path names in alternates.  If the "*" is used, then
  626.  * this matches the address against the user's current login and -any- path.
  627.  *
  628.  * Note that the alternates list is an array of addresses stored *reversed*!
  629.  */
  630. take_me_off(str)
  631. char *str;
  632. {
  633.     int i = 0, rm_me;
  634.     char tmp[256], addr[256], buf[HDRSIZ], *start = str;
  635.     register char *p, *p2, *b = buf;
  636.  
  637.     if (!str || !*str || do_set(set_options, "metoo"))
  638.     return;
  639.  
  640.     Debug("take_me_off()\n");
  641.     *b = 0;
  642.     do  {
  643.     /* get the first "addres" and advance p to next addres (ignore name) */
  644.     if (!(p = get_name_n_addr(str, NULL, tmp)))
  645.         break; /* we've reached the end of the address list */
  646.     rm_me = FALSE;
  647.     /* see if user's login is in the address */
  648.     if (!strcmp(login, tmp))
  649.         rm_me = TRUE;
  650.     else {
  651.         /* put address in !-format and store in "addr" */
  652.         (void) bang_form(addr, tmp);
  653.         (void) reverse(addr);
  654.         for (i = 0; alternates && alternates[i] && !rm_me; i++) {
  655.         if (alternates[i][0] == '*' && alternates[i][1] == '\0') {
  656.             (void) strcpy(tmp+1, login), tmp[0] = '!';
  657.             p2 = reverse(tmp);
  658.         } else
  659.             p2 = alternates[i];
  660.         if (!lcase_strncmp(p2, addr, strlen(p2))) {
  661.             Debug("\t%s\n", reverse(addr));
  662.             rm_me = TRUE;
  663.         }
  664.         }
  665.         for (i = 0; !rm_me && ourname && ourname[i]; i++) {
  666.         p2 = tmp + Strcpy(tmp, ourname[i]);
  667.         *p2++ = '!';
  668.         (void) strcpy(p2, login);
  669.         reverse(tmp);
  670.         if (!lcase_strncmp(tmp, addr, strlen(tmp))) {
  671.             Debug("\t%s\n", reverse(addr));
  672.             rm_me = TRUE;
  673.         }
  674.         }
  675.     }
  676.     /* The address is not the user's -- put it into the returned list */
  677.     if (!rm_me) {
  678.         char c = *p;
  679.         *p = 0;
  680.         b += Strcpy(b, str);
  681.         *p = c;
  682.     }
  683.     while (*p == ',' || isspace(*p))
  684.         p++;
  685.     if (*p && !rm_me)
  686.         *b++ = ',', *b++ = ' ', *b = '\0';
  687.     } while (*(str = p));
  688.     for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  689.     *b = 0;
  690.     (void) strcpy(start, buf);
  691. }
  692.  
  693. /*
  694.  * Place commas in between all addresses that don't already have
  695.  * them.  Addresses which use comments which are in parens or _not_
  696.  * within angle brackets *must* already have commas around them or
  697.  * you can't determine what is a comment and what is an address.
  698.  */
  699. fix_up_addr(str)
  700. char *str;
  701. {
  702.     char buf[HDRSIZ], *start = str;
  703.     register char c, *p, *b = buf;
  704.  
  705.     *b = 0;
  706.     do  {
  707.     /* get_name returns a pointer to the next address */
  708.     if (!(p = get_name_n_addr(str, NULL, NULL)))
  709.         break;
  710.     c = *p, *p = 0;
  711.     if (strlen(str) + (b - buf) >= sizeof(buf) - 2) {
  712.         /* print("Address too long! Lost address: \"%s\"\n", str); */
  713.         *p = c;
  714.         break;
  715.     }
  716.     for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  717.         *b = 0;
  718.     for (*p = c; *p == ',' || isspace(*p); p++)
  719.         ;
  720.     if (*p)
  721.         *b++ = ',', *b++ = ' ', *b = '\0';
  722.     } while (*(str = p));
  723.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  724.     *b = 0;
  725.     (void) strcpy(start, buf);
  726. }
  727.  
  728. /*
  729.  * Remove redundant addresses.
  730.  * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
  731.  */
  732. rm_redundant_addrs(to, cc)
  733. char *to, *cc;
  734. {
  735.     char tmp[256], addr[256], buf[HDRSIZ];
  736.     char **list; /* a list of addresses for comparison */
  737.     int list_cnt = 0, l;
  738.     register char c, *p, *b = buf, *start = to;
  739.     extern char *calloc();
  740.  
  741.     Debug("rm_redundant_addrs()\n");
  742.     list = (char **) calloc(256, sizeof(char *));
  743.     /* first do the To header */
  744.     do  {
  745.     /* get_name returns a pointer to the next address */
  746.     if (!(p = get_name_n_addr(to, NULL, tmp)))
  747.         break;
  748.     c = *p, *p = 0;
  749.     (void) bang_form(addr, tmp);
  750.     for (l = 0; l < list_cnt; l++)
  751.         if (!lcase_strncmp(addr, list[l], -1))
  752.         break;
  753.     /* if l == list_cnt, we got a new address, store it and add to buf */
  754.     if (l == list_cnt) {
  755.         /* Don't overwrite buffer. */
  756.         if (list_cnt < 256)
  757.         list[list_cnt++] = savestr(addr);
  758.         if (b > buf)
  759.         *b++ = ',', *b++ = ' ', *b = '\0';
  760.         for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
  761.         *b = 0;
  762.     } else
  763.         Debug("\t%s\n", tmp); /* already specified (removed from list) */
  764.     for (*p = c; *p == ',' || isspace(*p); p++)
  765.         ;
  766.     } while (*(to = p));
  767.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  768.     *b = 0;
  769.     (void) strcpy(start, buf);
  770.     b = buf, *b = 0;
  771.     /* Now do the Cc header.  If addr is listed in the To field, rm it in cc */
  772.     start = cc;
  773.     do  {
  774.     /* get_name returns a pointer to the next address */
  775.     if (!(p = get_name_n_addr(cc, NULL, tmp)))
  776.         break;
  777.     c = *p, *p = 0;
  778.     (void) bang_form(addr, tmp);
  779.     for (l = 0; l < list_cnt; l++)
  780.         if (!lcase_strncmp(addr, list[l], -1))
  781.         break;
  782.     if (l == list_cnt) {
  783.         /* Don't overwrite buffer. */
  784.         if (list_cnt < sizeof(list)/sizeof(char *))
  785.         list[list_cnt++] = savestr(addr);
  786.         if (b > buf)
  787.         *b++ = ',', *b++ = ' ', *b = '\0';
  788.         for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
  789.         *b = 0;
  790.     } else
  791.         Debug("\t%s\n", tmp); /* already specified (removed from list) */
  792.     for (*p = c; *p == ',' || isspace(*p); p++)
  793.         ;
  794.     } while (*(cc = p));
  795.     list[list_cnt] = NULL; /* for free_vec */
  796.     free_vec(list);
  797.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  798.     *b = 0;
  799.     (void) strcpy(start, buf);
  800. }
  801.  
  802. /*
  803.  * Get address and name from a string (str) which came from an address header
  804.  * in a message or typed by the user.  The string may contain one or more
  805.  * well-formed addresses.  Each must be separated by a comma.
  806.  *
  807.  * address, address, address
  808.  * address (comment or name here)
  809.  * comment or name <address>
  810.  * "Comment, even those with comma's!" <address>
  811.  * address (comma, (more parens), etc...)
  812.  *
  813.  * This does *not* handle cases like:
  814.  *    comment <address (comment)>
  815.  *
  816.  * find the *first* address here and return a pointer to the end of the
  817.  * address (usually a comma).  Return NULL on error: non-matching parens,
  818.  * brackets, quotes...
  819.  */
  820. char *
  821. get_name_n_addr(str, name, addr)
  822. register char *str, *name, *addr;
  823. {
  824.     register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  825.  
  826.     if (addr)
  827.     *addr = 0;
  828.     if (name)
  829.     *name = 0;
  830.     if (!str || !*str)
  831.     return NULL;
  832.  
  833.     /* first check to see if there's something to look for */
  834.     if (!(p = any(str, ",(<\""))) {
  835.     /* no comma or indication of a quote character. Find a space and
  836.      * return that.  If nothing, the entire string is a complete address
  837.      */
  838.     while (isspace(*str))
  839.         str++;
  840.     if (p = any(str, " \t"))
  841.         c = *p, *p = 0;
  842.     if (addr)
  843.         (void) strcpy(addr, str);
  844.     if (p)
  845.         *p = c;
  846.     return p? p : str + strlen(str);
  847.     }
  848.  
  849.     /* comma terminated before any comment stuff.  If so, check for whitespace
  850.      * before-hand cuz it's possible that strings aren't comma separated yet
  851.      * and they need to be.
  852.      *
  853.      * address address address, address
  854.      *                        ^p  <- p points here.
  855.      *        ^p2 <- should point here.
  856.      */
  857.     if (*p == ',') {
  858.     c = *p, *p = 0;
  859.     if (p2 = any(str, " \t"))
  860.         *p = ',', c = *p2, p = p2;
  861.     if (addr)
  862.         (void) strcpy(addr, str);
  863.     *p = c;
  864.     return p;
  865.     }
  866.  
  867.     /* starting to get hairy -- we found an angle bracket. This means that
  868.      * everything outside of those brackets are comments until we find that
  869.      * all important comma.  A comment AFTER the <addr> :
  870.      *  <address> John Doe
  871.      * can't call this function recursively or it'll think that "John Doe"
  872.      * is a string with two legal address on it (each name being an address).
  873.      */
  874.     if (*p == '<') { /* note that "str" still points to comment stuff! */
  875.     if (name && *str) {
  876.         *p = 0;
  877.         name += Strcpy(name, str);
  878.         *p = '<';
  879.     }
  880.     if (!(p2 = index(p+1, '>'))) {
  881.         wprint("Warning! Malformed address: \"%s\"\n", str);
  882.         return NULL;
  883.     }
  884.     if (addr) {
  885.         /* to support <addr (comment)> style addresses, add code here */
  886.         *p2 = 0;
  887.         skipspaces(1);
  888.         addr += Strcpy(addr, p);
  889.         while (addr > beg_addr && isspace(*(addr-1)))
  890.         *--addr = 0;
  891.         *p2 = '>';
  892.     }
  893.     /* take care of the case "... <addr> com (ment)" */
  894.     {
  895.         int p_cnt = 0; /* parenthesis counter */
  896.         p = p2;
  897.         /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  898.         for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  899.         if (p[1] == '(')
  900.             p_cnt++;
  901.         else if (p[1] == ')')
  902.             p_cnt--;
  903.         if (name)
  904.             *name++ = p[1];
  905.         }
  906.         if (p_cnt) {
  907.         wprint("Warning! Malformed name: \"%s\"\n", name);
  908.         return NULL;
  909.         }
  910.     }
  911.     if (name && name > beg_name) {
  912.         while (isspace(*(name-1)))
  913.         --name;
  914.         *name = 0;
  915.     }
  916.     }
  917.  
  918.     /* this is the worst -- now we have parentheses/quotes.  These guys can
  919.      * recurse pretty badly and contain commas within them.
  920.      */
  921.     if (*p == '(' || *p == '"') {
  922.     char *start = p;
  923.     int comment = 1;
  924.     c = *p;
  925.     /* "str" points to address while p points to comments */
  926.     if (addr && *str) {
  927.         *p = 0;
  928.         while (isspace(*str))
  929.         str++;
  930.         addr += Strcpy(addr, str);
  931.         while (addr > beg_addr && isspace(*(addr-1)))
  932.         *--addr = 0;
  933.         *p = c;
  934.     }
  935.     while (comment) {
  936.         if (c == '"' && !(p = index(p+1, '"')) ||
  937.         c == '(' && !(p = any(p+1, "()"))) {
  938.         wprint("Warning! Malformed address: \"%s\"\n", str);
  939.         return NULL;
  940.         }
  941.         if (*p == '(') /* loop again on parenthesis. quote ends loop */
  942.         comment++;
  943.         else
  944.         comment--;
  945.     }
  946.     /* Something like ``Comment (Comment) <addr>''.  In this case
  947.      * the name should include both comment parts with the
  948.      * parenthesis.   We have to redo addr.
  949.      */
  950.     if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  951.         if (!(p = index(p2, '>'))) {
  952.         wprint("Warning! Malformed address: \"%s\"\n", str);
  953.         return NULL;
  954.         }
  955.         if (addr = beg_addr) { /* reassign addr and compare to null */
  956.         c = *p; *p = 0;
  957.         addr += Strcpy(addr, p2+1);
  958.         while (addr > beg_addr && isspace(*(addr-1)))
  959.             *--addr = 0;
  960.         *p = c;
  961.         }
  962.         if (name) {
  963.         c = *p2; *p2 = 0;
  964.         name += Strcpy(name, str);
  965.         while (name > beg_name && isspace(*(name-1)))
  966.             *--name = 0;
  967.         *p2 = c;
  968.         }
  969.     } else if (name && start[1]) {
  970.         c = *p, *p = 0; /* c may be ')' instead of '(' now */
  971.         name += Strcpy(name, start+1);
  972.         while (name > beg_name && isspace(*(name-1)))
  973.         *--name = 0;
  974.         *p = c;
  975.     }
  976.     }
  977.     skipspaces(1);
  978.     /* this is so common, save time by returning now */
  979.     if (!*p || *p == ',')
  980.     return p;
  981.     return get_name_n_addr(p, name, addr);
  982. }
  983.  
  984. /* takes string 's' which can be a name or list of names separated by
  985.  * commas and checks to see if each is aliased to something else.
  986.  * return address of the static buf.
  987.  */
  988. char *
  989. alias_to_address(s)
  990. register char *s;
  991. {
  992.     static char buf[HDRSIZ];
  993.     register char *p, *p2, *tmp;
  994.     char newbuf[HDRSIZ], c;
  995.     static int recursive;
  996.  
  997.     if (!aliases)
  998.     return strcpy(buf, s);
  999.     if (!s || !*s) {
  1000.     print("No recipients!?!\n");
  1001.     return NULL;
  1002.     }
  1003.     if (!recursive) {
  1004.     bzero(buf, sizeof buf);
  1005.     p2 = buf;  /* if we're starting all this, p2 starts at &buf[0] */
  1006.     } else
  1007.     p2 = buf+strlen(buf);   /* else, pick up where we left off */
  1008.  
  1009.     if (++recursive == 30) {
  1010.     print("alias references too many addresses!\n");
  1011.     recursive = 0;
  1012.     return NULL;
  1013.     }
  1014.     do  {
  1015.     char addr[256];
  1016.     if (!(p = get_name_n_addr(s, NULL, addr)))
  1017.         break;
  1018.     c = *p, *p = 0;
  1019.  
  1020.     /* On recursive calls, compare against the entire
  1021.      * previous expansion, not just the address part.
  1022.      */
  1023.     if (recursive > 1)
  1024.         (void) strcpy(addr, s);
  1025.  
  1026.     /* if this is an alias, recurse this routine to expand it out */
  1027.     if ((tmp = do_set(aliases, addr)) && *tmp) {
  1028.         if (!alias_to_address(strcpy(newbuf, tmp))) {
  1029.         *p = c;
  1030.         return NULL;
  1031.         } else
  1032.         p2 = buf+strlen(buf);
  1033.     /* Now, make sure the buffer doesn't overflow */
  1034.     } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) {  /* add ", " */
  1035.         print("address length too long.\n");
  1036.         recursive = 0;
  1037.         *p = c;
  1038.         return NULL;
  1039.     } else {
  1040.         /* append the new alias (or unchanged address) onto the buffer */
  1041.         p2 += Strcpy(p2, s);
  1042.         *p2++ = ',', *p2++ = ' ', *p2 = '\0';
  1043.     }
  1044.     for (*p = c; *p == ',' || isspace(*p); p++)
  1045.         ;
  1046.     } while (*(s = p));
  1047.     if (recursive)
  1048.     recursive--;
  1049.     if (!recursive)
  1050.     *(p2-2) = 0;  /* get rid of last ", " if end of recursion */
  1051.     return buf;
  1052. }
  1053.  
  1054. /*
  1055.  * Wrap addresses so that the headers don't exceed n chars (typically 80).
  1056.  */
  1057. char *
  1058. wrap_addrs(str, n)
  1059. char *str;
  1060. {
  1061.     char buf[HDRSIZ * 2], *start = str;
  1062.     register char *b = buf, *p, c, *line_start = buf;
  1063.  
  1064.     *b = 0;
  1065.     do  {
  1066.     /* get_name returns a pointer to the next address */
  1067.     if (!(p = get_name_n_addr(str, NULL, NULL)))
  1068.         break;
  1069.     c = *p, *p = 0;
  1070.     if (b > buf) {
  1071.         *b++ = ',', *b++ = ' ', *b = '\0';
  1072.         if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
  1073.         *b++ = '\n', *b++ = '\t', line_start = b;
  1074.     }
  1075.     for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  1076.         *b = 0;
  1077.     for (*p = c; *p == ',' || isspace(*p); p++)
  1078.         ;
  1079.     } while (*(str = p));
  1080.     for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  1081.     *b = 0;
  1082.     return strcpy(start, buf);
  1083. }
  1084.