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