home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65c+IDA-1.4.4.1 / src / recipient.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-08  |  16.6 KB  |  736 lines

  1. /*
  2.  * Copyright (c) 1983 Eric P. Allman
  3.  * Copyright (c) 1988 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted provided
  7.  * that: (1) source distributions retain this entire copyright notice and
  8.  * comment, and (2) distributions including binaries display the following
  9.  * acknowledgement:  ``This product includes software developed by the
  10.  * University of California, Berkeley and its contributors'' in the
  11.  * documentation or other materials provided with the distribution and in
  12.  * all advertising materials mentioning features or use of this software.
  13.  * Neither the name of the University nor the names of its contributors may
  14.  * be used to endorse or promote products derived from this software without
  15.  * specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  17.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  18.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. #ifndef lint
  22. static char sccsid[] = "@(#)recipient.c    5.18 (Berkeley) 6/1/90";
  23. static char  rcsid[] = "@(#)$Id: recipient.c,v 5.18.0.13 1991/08/08 22:13:13 paul Exp $";
  24. #endif /* not lint */
  25.  
  26. #include "sendmail.h"
  27. #include <sys/stat.h>
  28. #include <pwd.h>
  29. #ifndef S_IEXEC
  30. # define    S_IEXEC        _S_IEXEC
  31. #endif /* !S_IEXEC */
  32. #ifndef S_IWRITE
  33. # define    S_IWRITE    _S_IWRITE
  34. #endif /* !S_IWRITE */
  35.  
  36. #ifdef __STDC__
  37. static struct passwd * finduser(char *);
  38. static int partialstring(const char *, const char *);
  39. static bool writable(struct stat *);
  40. #else /* !__STDC__ */
  41. static struct passwd * finduser();
  42. static int partialstring();
  43. static bool writable();
  44. #endif /* __STDC__ */
  45.  
  46. /*
  47. **  SENDTOLIST -- Designate a send list.
  48. **
  49. **    The parameter is a comma-separated list of people to send to.
  50. **    This routine arranges to send to all of them.
  51. **
  52. **    Parameters:
  53. **        list -- the send list.
  54. **        ctladdr -- the address template for the person to
  55. **            send to -- effective uid/gid are important.
  56. **            This is typically the alias that caused this
  57. **            expansion.
  58. **        sendq -- a pointer to the head of a queue to put
  59. **            these people into.
  60. **
  61. **    Returns:
  62. **        none
  63. **
  64. **    Side Effects:
  65. **        none.
  66. */
  67.  
  68. # define MAXRCRSN    10
  69.  
  70. void
  71. sendtolist(list, ctladdr, sendq)
  72.     const char *list;
  73.     ADDRESS *ctladdr;
  74.     ADDRESS **sendq;
  75. {
  76.     register const char *p;
  77.     register ADDRESS *al;    /* list of addresses to send to */
  78.     bool firstone;        /* set on first address sent */
  79.     bool selfref;        /* set if this list includes ctladdr */
  80.     char delimiter;        /* the address delimiter */
  81.  
  82.     if (tTd(25, 1))
  83.     {
  84.         printf("sendto: %s\n   ctladdr=", list);
  85.         printaddr(ctladdr, FALSE);
  86.     }
  87.  
  88.     /* heuristic to determine old versus new style addresses */
  89.     if (ctladdr == NULL &&
  90.         (index(list, ',') != NULL || index(list, ';') != NULL ||
  91.          index(list, '<') != NULL || index(list, '(') != NULL))
  92.         CurEnv->e_flags &= ~EF_OLDSTYLE;
  93.     delimiter = ' ';
  94.     if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
  95.         delimiter = ',';
  96.  
  97.     firstone = TRUE;
  98.     selfref = FALSE;
  99.     al = NULL;
  100.  
  101.     for (p = list; *p != '\0'; )
  102.     {
  103.         register ADDRESS *a;
  104.         extern char *DelimChar;        /* defined in prescan */
  105.  
  106.         /* parse the address */
  107.         while (isspace(*p) || *p == ',')
  108.             p++;
  109.         a = parseaddr((char *)p, (ADDRESS *) NULL, 1, delimiter);
  110.         p = DelimChar;
  111.         if (a == NULL)
  112.             continue;
  113.         a->q_next = al;
  114.         a->q_alias = ctladdr;
  115.  
  116.         /* see if this should be marked as a primary address */
  117.         if (ctladdr == NULL ||
  118.             (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
  119.             a->q_flags |= QPRIMARY;
  120.  
  121.         /* put on send queue or suppress self-reference */
  122.         if (ctladdr != NULL && sameaddr(ctladdr, a))
  123.             selfref = TRUE;
  124.         else
  125.             al = a;
  126.         firstone = FALSE;
  127.     }
  128.  
  129.     /* if this alias doesn't include itself, delete ctladdr */
  130.     if (selfref && ctladdr != NULL)
  131.         ctladdr->q_flags |= QSELFREF;
  132.  
  133.     /* arrange to send to everyone on the local send list */
  134.     while (al != NULL)
  135.     {
  136.         register ADDRESS *a = al;
  137.  
  138.         al = a->q_next;
  139.         setctladdr(a);
  140.         a = recipient(a, sendq);
  141.  
  142.         /* arrange to inherit full name */
  143.         if (a->q_fullname == NULL && ctladdr != NULL)
  144.             a->q_fullname = ctladdr->q_fullname;
  145.     }
  146.  
  147.     CurEnv->e_to = NULL;
  148. }
  149. /*
  150. **  RECIPIENT -- Designate a message recipient
  151. **
  152. **    Saves the named person for future mailing.
  153. **
  154. **    Parameters:
  155. **        a -- the (preparsed) address header for the recipient.
  156. **        sendq -- a pointer to the head of a queue to put the
  157. **            recipient in.  Duplicate supression is done
  158. **            in this queue.
  159. **
  160. **    Returns:
  161. **        The actual address in the queue.  This will be "a" if
  162. **        the address is not a duplicate, else the original address.
  163. **
  164. **    Side Effects:
  165. **        none.
  166. */
  167.  
  168. ADDRESS *
  169. recipient(a, sendq)
  170.     register ADDRESS *a;
  171.     register ADDRESS **sendq;
  172. {
  173.     register ADDRESS *q;
  174.     ADDRESS **pq;
  175.     register struct mailer *m;
  176.     register char *p;
  177.     bool quoted = FALSE;        /* set if the addr has a quote bit */
  178.     char buf[MAXNAME];        /* unquoted image of the user name */
  179.  
  180.     CurEnv->e_to = a->q_paddr;
  181.     m = a->q_mailer;
  182.     errno = 0;
  183.     if (tTd(26, 1))
  184.     {
  185.         printf("\nrecipient: ");
  186.         printaddr(a, FALSE);
  187.     }
  188.  
  189.     /* break aliasing loops */
  190.     if (AliasLevel > MAXRCRSN)
  191.     {
  192.         usrerr("aliasing/forwarding loop broken");
  193.         return (a);
  194.     }
  195.  
  196.     /*
  197.     **  Finish setting up address structure.
  198.     */
  199.  
  200.     /* set the queue timeout */
  201.     a->q_timeout = TimeOut;
  202.  
  203.     /* map user & host to lower case if requested on non-aliases */
  204.     if (a->q_alias == NULL)
  205.         loweraddr(a);
  206.  
  207.     /* get unquoted user for file, program or user.name check */
  208.     (void) strcpy(buf, a->q_user);
  209.     for (p = buf; *p != '\0' && !quoted; p++)
  210.     {
  211.         if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
  212.             quoted = TRUE;
  213.     }
  214.     stripquotes(buf, TRUE);
  215.  
  216.     /* do sickly crude mapping for program mailing, etc. */
  217.     if (m == LocalMailer && buf[0] == '|')
  218.     {
  219.         a->q_mailer = m = ProgMailer;
  220.         a->q_user++;
  221.         if (a->q_alias == NULL && !QueueRun && !ForceMail)
  222.         {
  223.             a->q_flags |= QDONTSEND|QBADADDR;
  224.             usrerr("Cannot mail directly to programs");
  225.         }
  226.     }
  227.  
  228.     /*
  229.     **  Look up this person in the recipient list.
  230.     **    If they are there already, return, otherwise continue.
  231.     **    If the list is empty, just add it.  Notice the cute
  232.     **    hack to make from addresses suppress things correctly:
  233.     **    the QDONTSEND bit will be set in the send list.
  234.     **    [Please note: the emphasis is on "hack."]
  235.     */
  236.  
  237.     for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
  238.     {
  239.         if (!ForceMail && sameaddr(q, a))
  240.         {
  241.             if (tTd(26, 1))
  242.             {
  243.                 printf("%s in sendq: ", a->q_paddr);
  244.                 printaddr(q, FALSE);
  245.             }
  246.             if (!bitset(QDONTSEND, a->q_flags))
  247.                 message(Arpa_Info, "duplicate suppressed");
  248.             if (!bitset(QPRIMARY, q->q_flags))
  249.                 q->q_flags |= a->q_flags;
  250.             return (q);
  251.         }
  252.     }
  253.  
  254.     /* add address on list */
  255.     *pq = a;
  256.     a->q_next = NULL;
  257.     CurEnv->e_nrcpts++;
  258.  
  259.     /*
  260.     **  Alias the name and handle :include: specs.
  261.     */
  262.  
  263.     if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
  264.     {
  265.         if (strncmp(a->q_user, ":include:", 9) == 0)
  266.         {
  267.             a->q_flags |= QDONTSEND;
  268.             if (a->q_alias == NULL && !QueueRun && !ForceMail)
  269.             {
  270.                 a->q_flags |= QBADADDR;
  271.                 usrerr("Cannot mail directly to :include:s");
  272.             }
  273.             else
  274.             {
  275.                 message(Arpa_Info, "including file %s", &a->q_user[9]);
  276.                 include(&a->q_user[9], " sending", a, sendq);
  277.             }
  278.         }
  279.         else
  280.             alias(a, sendq);
  281.     }
  282.  
  283.     /*
  284.     **  If the user is local and still being sent, verify that
  285.     **  the address is good.  If it is, try to forward.
  286.     **  If the address is already good, we have a forwarding
  287.     **  loop.  This can be broken by just sending directly to
  288.     **  the user (which is probably correct anyway).
  289.     */
  290.  
  291.     if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
  292.     {
  293.         struct stat stb;
  294.  
  295.         /* see if this is to a file */
  296.         if (buf[0] == '/')
  297.         {
  298.             p = rindex(buf, '/');
  299.             /* check if writable or creatable */
  300.             if (a->q_alias == NULL && !QueueRun && !ForceMail)
  301.             {
  302.                 a->q_flags |= QDONTSEND|QBADADDR;
  303.                 usrerr("Cannot mail directly to files");
  304.             }
  305.             else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
  306.                 (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
  307.             {
  308.                 a->q_flags |= QBADADDR;
  309.                 giveresponse(EX_CANTCREAT, m, CurEnv);
  310.             }
  311.         }
  312.         else
  313.         {
  314.             register struct passwd *pw;
  315.  
  316.             /* warning -- finduser may trash buf */
  317.             pw = finduser(buf);
  318.             if (pw == NULL)
  319.             {
  320.                 a->q_flags |= QBADADDR;
  321.                 errno = 0;    /* no special error */
  322.                 giveresponse(EX_NOUSER, m, CurEnv);
  323.             }
  324.             else
  325.             {
  326.                 char nbuf[MAXNAME];
  327.  
  328.                 if (strcmp(a->q_user, pw->pw_name) != 0)
  329.                 {
  330.                     a->q_user = newstr(pw->pw_name);
  331.                     (void) strcpy(buf, pw->pw_name);
  332.                 }
  333.                 a->q_home = newstr(pw->pw_dir);
  334.                 a->q_uid = pw->pw_uid;
  335.                 a->q_gid = pw->pw_gid;
  336.                 a->q_flags |= QGOODUID;
  337.                 buildfname(pw->pw_gecos, pw->pw_name, nbuf);
  338.                 if (nbuf[0] != '\0')
  339.                     a->q_fullname = newstr(nbuf);
  340.                 if (!quoted)
  341.                     forward(a, sendq);
  342.             }
  343.         }
  344.     }
  345.     return (a);
  346. }
  347. /*
  348. **  FINDUSER -- find the password entry for a user.
  349. **
  350. **    This looks a lot like getpwnam, except that it may want to
  351. **    do some fancier pattern matching in /etc/passwd.
  352. **
  353. **    This routine contains most of the time of many sendmail runs.
  354. **    It deserves to be optimized.
  355. **
  356. **    Parameters:
  357. **        name -- the name to match against.
  358. **
  359. **    Returns:
  360. **        A pointer to a pw struct.
  361. **        NULL if name is unknown or ambiguous.
  362. **
  363. **    Side Effects:
  364. **        may modify name.
  365. */
  366.  
  367. #define WORST_MATCH    -2        /* even worse than no match */
  368. #define NO_UID        -999        /* any "impossible" uid will do */
  369.  
  370. static struct passwd *
  371. finduser(name)
  372.     char *name;
  373. {
  374.     register struct passwd *pw;
  375. #ifdef FUZZY
  376.     int best_match = WORST_MATCH;
  377.     int best_uid = NO_UID;
  378. #endif /* FUZZY */
  379.  
  380.     errno = 0;
  381.  
  382.     /* first try name as given */
  383.     if ((pw = getpwnam(name)) == NULL)
  384.     {
  385.         /* try again as lower-case */
  386.         char *lowname = newstr(name);
  387.         makelower(lowname);
  388.         pw = getpwnam(lowname);
  389.         free(lowname);
  390.     }
  391.  
  392.     if (tTd(26, 6))
  393.         printf("%s password entry for \"%s\"\n",
  394.                pw ? "found" : "can't find", name);
  395.  
  396. #ifndef FUZZY
  397.     return (pw);
  398. #else /* FUZZY */
  399.     if (pw != NULL)
  400.         return (pw);
  401.  
  402.     if (tTd(26, 6))
  403.         printf("looking for partial match to \"%s\"\n", name);
  404.     (void) setpwent();
  405.     while ((pw = getpwent()) != NULL)
  406.     {
  407.         char buf[MAXNAME];
  408.         register int this_match;
  409.  
  410.         if (strcasecmp(pw->pw_name, name) == 0)
  411.         {
  412.             if (tTd(26, 6))
  413.             printf("found password entry for \"%s\" as \"%s\"\n",
  414.                    name, pw->pw_name);
  415.             return (pw);
  416.         }
  417.         buildfname(pw->pw_gecos, pw->pw_name, buf);
  418.         this_match = partialstring(buf, name);
  419.         if (tTd(26, 6) && this_match >= 0)
  420.             printf("matched on level %d with \"%s\"\n",
  421.                    this_match, buf);
  422.         if (this_match < best_match)
  423.             continue;
  424.         else if (this_match > best_match)
  425.         {
  426.             best_match = this_match;
  427.             best_uid = pw->pw_uid;
  428.         }
  429.         else if (best_uid != pw->pw_uid)
  430.             best_uid = NO_UID;
  431.     }
  432.     if (tTd(26, 6))
  433.     {
  434.         if (best_match == WORST_MATCH)
  435.             printf("no match, failing...\n");
  436.         else if (best_uid == NO_UID)
  437.             printf("ambiguous match, failing...\n");
  438.         else
  439.             printf("succeding on level %d...\n",
  440.                    best_match);
  441.     }
  442.     if (best_uid == NO_UID)
  443.         return (NULL);
  444.  
  445.     pw = getpwuid(best_uid);
  446.             message(Arpa_Info, "sending to login name %s", pw->pw_name);
  447.             return (pw);
  448. #endif /* !FUZZY */
  449. }
  450. /*
  451. **  PARTIALSTRING -- is one string of words contained by another?
  452. **
  453. **    See if one string of words can be found as part of
  454. **    another string of words.  All substrings delimited by
  455. **    one or more non-alphanumeric characters are considered
  456. **    "words", and a partial match is such that all the words
  457. **    of the pattern string are either full prefixes
  458. **    of the target string.  Upper or lower case letters are
  459. **    considered equal.
  460. **
  461. **    Parameters:
  462. **        target -- target string
  463. **        pattern -- pattern string
  464. **
  465. **    Returns:
  466. **        The number of fully matched words, or -1 if none.
  467. **
  468. **    Side Effects:
  469. **        None.
  470. **
  471. */
  472.  
  473. #ifdef FUZZY
  474. static int
  475. partialstring(target, pattern)
  476.     const char *target, *pattern;
  477. {
  478.     register char *t, *p, *q;
  479.     int full_words = 0;
  480.  
  481.     /* skip initial delimiters */
  482.     for (t = (char *)target; *t != '\0' && !isalnum(*t); t++)
  483.         ;
  484.     for (p = (char *)pattern; *p != '\0' && !isalnum(*p); p++)
  485.         ;
  486.     q = p;
  487.  
  488.     while (*t != '\0' && *p != '\0')
  489.     {
  490.         /*
  491.          * if at end of pattern word, find next, remember it,
  492.          * and eat the current target word
  493.          */
  494.         if (!isalnum(*p))
  495.         {
  496.             while (*p != '\0' && !isalnum(*p))
  497.                 p++;
  498.             if (*p == '\0')
  499.                 continue;
  500.             q = p;
  501.             if (!isalnum(*t))
  502.                 full_words++;
  503.             while (*t != '\0' && isalnum(*t))
  504.                 t++;
  505.             while (*t != '\0' && !isalnum(*t))
  506.                 t++;
  507.             continue;
  508.         }
  509.  
  510.         /*
  511.          * if match, advance both pointers
  512.          */
  513.         if ((isupper(*t) ? tolower(*t) : *t) ==
  514.             (isupper(*p) ? tolower(*p) : *p))
  515.         {
  516.             t++, p++;
  517.             continue;
  518.         }
  519.  
  520.         /*
  521.          * if no match, backtrack to last unmatched pattern word and
  522.          * eat current target word.
  523.          */
  524.         p = q;
  525.         while (*t != '\0' && isalnum(*t))
  526.             t++;
  527.         while (*t != '\0' && !isalnum(*t))
  528.             t++;
  529.     }
  530.  
  531.     /*
  532.      * now, the pattern should be fully consumed if there was a match
  533.      */
  534.     if (*p == '\0')
  535.         return (isalnum(*t) ? full_words : full_words + 1);
  536.     else
  537.         return (-1);
  538. }
  539. #endif /* FUZZY */
  540. /*
  541. **  WRITABLE -- predicate returning if the file is writable.
  542. **
  543. **    This routine must duplicate the algorithm in sys/fio.c.
  544. **    Unfortunately, we cannot use the access call since we
  545. **    won't necessarily be the real uid when we try to
  546. **    actually open the file.
  547. **
  548. **    Notice that ANY file with ANY execute bit is automatically
  549. **    not writable.  This is also enforced by mailfile.
  550. **
  551. **    Parameters:
  552. **        s -- pointer to a stat struct for the file.
  553. **
  554. **    Returns:
  555. **        TRUE -- if we will be able to write this file.
  556. **        FALSE -- if we cannot write this file.
  557. **
  558. **    Side Effects:
  559. **        none.
  560. */
  561.  
  562. static bool
  563. writable(s)
  564.     register struct stat *s;
  565. {
  566.     int euid, egid;
  567.     int bits;
  568.  
  569.     if (bitset(0111, s->st_mode))
  570.         return (FALSE);
  571.     euid = getruid();
  572.     egid = getrgid();
  573.     if (geteuid() == 0)
  574.     {
  575.         if (bitset(S_ISUID, s->st_mode))
  576.             euid = s->st_uid;
  577.         if (bitset(S_ISGID, s->st_mode))
  578.             egid = s->st_gid;
  579.     }
  580.  
  581.     if (euid == 0)
  582.         return (TRUE);
  583.     bits = S_IWRITE;
  584.     if (euid != s->st_uid)
  585.     {
  586.         bits >>= 3;
  587.         if (egid != s->st_gid)
  588.             bits >>= 3;
  589.     }
  590.     return ((s->st_mode & bits) != 0);
  591. }
  592. /*
  593. **  INCLUDE -- handle :include: specification.
  594. **
  595. **    Parameters:
  596. **        fname -- filename to include.
  597. **        msg -- message to print in verbose mode.
  598. **        ctladdr -- address template to use to fill in these
  599. **            addresses -- effective user/group id are
  600. **            the important things.
  601. **        sendq -- a pointer to the head of the send queue
  602. **            to put these addresses in.
  603. **
  604. **    Returns:
  605. **        none.
  606. **
  607. **    Side Effects:
  608. **        reads the :include: file and sends to everyone
  609. **        listed in that file.
  610. */
  611.  
  612. void
  613. include(fname, msg, ctladdr, sendq)
  614.     const char *fname, *msg;
  615.     ADDRESS *ctladdr;
  616.     ADDRESS **sendq;
  617. {
  618.     char buf[MAXLINE];
  619.     register FILE *fp;
  620.     char *oldto = CurEnv->e_to;
  621.     char *oldfilename = FileName;
  622.     int oldlinenumber = LineNumber;
  623.     bool included = FALSE;
  624.  
  625.     fp = fopen(fname, "r");
  626.     if (fp == NULL)
  627.     {
  628.         usrerr("Cannot open %s", fname);
  629.         return;
  630.     }
  631.     if (getctladdr(ctladdr) == NULL)
  632.     {
  633.         struct stat st;
  634.  
  635.         if (fstat(fileno(fp), &st) < 0)
  636.             syserr("Cannot fstat %s!", fname);
  637.         ctladdr->q_uid = st.st_uid;
  638.         ctladdr->q_gid = st.st_gid;
  639.         ctladdr->q_flags |= QGOODUID;
  640.     }
  641.  
  642.     /* sendtolist() detects possible self-refs in any line of this file */
  643.     ctladdr->q_flags &= ~QSELFREF;
  644.  
  645.     /* read the file -- each line is a comma-separated list. */
  646.     FileName = (char *)fname;
  647.     LineNumber = 0;
  648.     while (fgets(buf, sizeof buf, fp) != NULL)
  649.     {
  650.         register char *p = index(buf, '\n');
  651.  
  652.         LineNumber++;
  653.         if (p != NULL)
  654.             *p = '\0';
  655.  
  656.         /* ignore null lines and # comment lines */
  657.         if ((buf[0] == '\0') || (buf[0] == '#'))
  658.             continue;
  659.         CurEnv->e_to = oldto;
  660.         message(Arpa_Info, "%s to %s", msg, buf);
  661.         AliasLevel++;
  662.         sendtolist(buf, ctladdr, sendq);
  663.         included = TRUE;
  664.         AliasLevel--;
  665.     }
  666.  
  667.     if (included && !bitset(QSELFREF, ctladdr->q_flags))
  668.         ctladdr->q_flags |= QDONTSEND;
  669.     (void) fclose(fp);
  670.     FileName = oldfilename;
  671.     LineNumber = oldlinenumber;
  672. }
  673. /*
  674. **  SENDTOARGV -- send to an argument vector.
  675. **
  676. **    Parameters:
  677. **        argv -- argument vector to send to.
  678. **
  679. **    Returns:
  680. **        none.
  681. **
  682. **    Side Effects:
  683. **        puts all addresses on the argument vector onto the
  684. **            send queue.
  685. */
  686.  
  687. void
  688. sendtoargv(argv)
  689.     register char **argv;
  690. {
  691.     register char *p;
  692.  
  693.     while ((p = *argv++) != NULL)
  694.     {
  695.         if (argv[0] != NULL && argv[1] != NULL && !strcasecmp(argv[0], "at"))
  696.         {
  697.             char nbuf[MAXNAME];
  698.  
  699.             if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
  700.                 usrerr("address overflow");
  701.             else
  702.             {
  703.                 (void) strcpy(nbuf, p);
  704.                 (void) strcat(nbuf, "@");
  705.                 (void) strcat(nbuf, argv[1]);
  706.                 p = newstr(nbuf);
  707.                 argv += 2;
  708.             }
  709.         }
  710.         sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
  711.     }
  712. }
  713. /*
  714. **  GETCTLADDR -- get controlling address from an address header.
  715. **
  716. **    If none, get one corresponding to the effective userid.
  717. **
  718. **    Parameters:
  719. **        a -- the address to find the controller of.
  720. **
  721. **    Returns:
  722. **        the controlling address.
  723. **
  724. **    Side Effects:
  725. **        none.
  726. */
  727.  
  728. ADDRESS *
  729. getctladdr(a)
  730.     register ADDRESS *a;
  731. {
  732.     while (a != NULL && !bitset(QGOODUID, a->q_flags))
  733.         a = a->q_alias;
  734.     return (a);
  735. }
  736.