home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / sendmail / src / headers.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-20  |  18.1 KB  |  865 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, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. All advertising materials mentioning features or use of this software
  15.  *    must display the following acknowledgement:
  16.  *    This product includes software developed by the University of
  17.  *    California, Berkeley and its contributors.
  18.  * 4. Neither the name of the University nor the names of its contributors
  19.  *    may be used to endorse or promote products derived from this software
  20.  *    without specific prior written permission.
  21.  *
  22.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32.  * SUCH DAMAGE.
  33.  */
  34.  
  35. #ifndef lint
  36. static char sccsid[] = "@(#)headers.c    5.15 (Berkeley) 6/1/90";
  37. #endif /* not lint */
  38.  
  39. # include <sys/param.h>
  40. # include <errno.h>
  41. # include "sendmail.h"
  42.  
  43. /*
  44. **  CHOMPHEADER -- process and save a header line.
  45. **
  46. **    Called by collect and by readcf to deal with header lines.
  47. **
  48. **    Parameters:
  49. **        line -- header as a text line.
  50. **        def -- if set, this is a default value.
  51. **
  52. **    Returns:
  53. **        flags for this header.
  54. **
  55. **    Side Effects:
  56. **        The header is saved on the header list.
  57. **        Contents of 'line' are destroyed.
  58. */
  59.  
  60. chompheader(line, def)
  61.     char *line;
  62.     bool def;
  63. {
  64.     register char *p;
  65.     register HDR *h;
  66.     HDR **hp;
  67.     char *fname;
  68.     char *fvalue;
  69.     struct hdrinfo *hi;
  70.     bool cond = FALSE;
  71.     BITMAP mopts;
  72.     extern char *crackaddr();
  73.  
  74.     if (tTd(31, 6))
  75.         printf("chompheader: %s\n", line);
  76.  
  77.     /* strip off options */
  78.     clrbitmap(mopts);
  79.     p = line;
  80.     if (*p == '?')
  81.     {
  82.         /* have some */
  83.         register char *q = index(p + 1, *p);
  84.         
  85.         if (q != NULL)
  86.         {
  87.             *q++ = '\0';
  88.             while (*++p != '\0')
  89.                 setbitn(*p, mopts);
  90.             p = q;
  91.         }
  92.         else
  93.             usrerr("chompheader: syntax error, line \"%s\"", line);
  94.         cond = TRUE;
  95.     }
  96.  
  97.     /* find canonical name */
  98.     fname = p;
  99.     p = index(p, ':');
  100.     if (p == NULL)
  101.     {
  102.         syserr("chompheader: syntax error, line \"%s\"", line);
  103.         return (0);
  104.     }
  105.     fvalue = &p[1];
  106.     while (isspace(*--p))
  107.         continue;
  108.     *++p = '\0';
  109.     makelower(fname);
  110.  
  111.     /* strip field value on front */
  112.     if (*fvalue == ' ')
  113.         fvalue++;
  114.  
  115.     /* see if it is a known type */
  116.     for (hi = HdrInfo; hi->hi_field != NULL; hi++)
  117.     {
  118.         if (strcmp(hi->hi_field, fname) == 0)
  119.             break;
  120.     }
  121.  
  122.     /* see if this is a resent message */
  123.     if (!def && bitset(H_RESENT, hi->hi_flags))
  124.         CurEnv->e_flags |= EF_RESENT;
  125.  
  126.     /* if this means "end of header" quit now */
  127.     if (bitset(H_EOH, hi->hi_flags))
  128.         return (hi->hi_flags);
  129.  
  130.     /* drop explicit From: if same as what we would generate -- for MH */
  131.     p = "resent-from";
  132.     if (!bitset(EF_RESENT, CurEnv->e_flags))
  133.         p += 7;
  134.     if (!def && !QueueRun && strcmp(fname, p) == 0)
  135.     {
  136.         if (CurEnv->e_from.q_paddr != NULL &&
  137.             strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
  138.             return (hi->hi_flags);
  139.     }
  140.  
  141.     /* delete default value for this header */
  142.     for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
  143.     {
  144.         if (strcmp(fname, h->h_field) == 0 &&
  145.             bitset(H_DEFAULT, h->h_flags) &&
  146.             !bitset(H_FORCE, h->h_flags))
  147.             h->h_value = NULL;
  148.     }
  149.  
  150.     /* create a new node */
  151.     h = (HDR *) xalloc(sizeof *h);
  152.     h->h_field = newstr(fname);
  153.     h->h_value = NULL;
  154.     h->h_link = NULL;
  155.     bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
  156.     *hp = h;
  157.     h->h_flags = hi->hi_flags;
  158.     if (def)
  159.         h->h_flags |= H_DEFAULT;
  160.     if (cond)
  161.         h->h_flags |= H_CHECK;
  162.     if (h->h_value != NULL)
  163.         free((char *) h->h_value);
  164.     h->h_value = newstr(fvalue);
  165.  
  166.     /* hack to see if this is a new format message */
  167.     if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
  168.         (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
  169.          index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
  170.     {
  171.         CurEnv->e_flags &= ~EF_OLDSTYLE;
  172.     }
  173.  
  174.     return (h->h_flags);
  175. }
  176. /*
  177. **  ADDHEADER -- add a header entry to the end of the queue.
  178. **
  179. **    This bypasses the special checking of chompheader.
  180. **
  181. **    Parameters:
  182. **        field -- the name of the header field.
  183. **        value -- the value of the field.  It must be lower-cased.
  184. **        e -- the envelope to add them to.
  185. **
  186. **    Returns:
  187. **        none.
  188. **
  189. **    Side Effects:
  190. **        adds the field on the list of headers for this envelope.
  191. */
  192.  
  193. addheader(field, value, e)
  194.     char *field;
  195.     char *value;
  196.     ENVELOPE *e;
  197. {
  198.     register HDR *h;
  199.     register struct hdrinfo *hi;
  200.     HDR **hp;
  201.  
  202.     /* find info struct */
  203.     for (hi = HdrInfo; hi->hi_field != NULL; hi++)
  204.     {
  205.         if (strcmp(field, hi->hi_field) == 0)
  206.             break;
  207.     }
  208.  
  209.     /* find current place in list -- keep back pointer? */
  210.     for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
  211.     {
  212.         if (strcmp(field, h->h_field) == 0)
  213.             break;
  214.     }
  215.  
  216.     /* allocate space for new header */
  217.     h = (HDR *) xalloc(sizeof *h);
  218.     h->h_field = field;
  219.     h->h_value = newstr(value);
  220.     h->h_link = *hp;
  221.     h->h_flags = hi->hi_flags | H_DEFAULT;
  222.     clrbitmap(h->h_mflags);
  223.     *hp = h;
  224. }
  225. /*
  226. **  HVALUE -- return value of a header.
  227. **
  228. **    Only "real" fields (i.e., ones that have not been supplied
  229. **    as a default) are used.
  230. **
  231. **    Parameters:
  232. **        field -- the field name.
  233. **
  234. **    Returns:
  235. **        pointer to the value part.
  236. **        NULL if not found.
  237. **
  238. **    Side Effects:
  239. **        none.
  240. */
  241.  
  242. char *
  243. hvalue(field)
  244.     char *field;
  245. {
  246.     register HDR *h;
  247.  
  248.     for (h = CurEnv->e_header; h != NULL; h = h->h_link)
  249.     {
  250.         if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
  251.             return (h->h_value);
  252.     }
  253.     return (NULL);
  254. }
  255. /*
  256. **  ISHEADER -- predicate telling if argument is a header.
  257. **
  258. **    A line is a header if it has a single word followed by
  259. **    optional white space followed by a colon.
  260. **
  261. **    Parameters:
  262. **        s -- string to check for possible headerness.
  263. **
  264. **    Returns:
  265. **        TRUE if s is a header.
  266. **        FALSE otherwise.
  267. **
  268. **    Side Effects:
  269. **        none.
  270. */
  271.  
  272. bool
  273. isheader(s)
  274.     register char *s;
  275. {
  276.     while (*s > ' ' && *s != ':' && *s != '\0')
  277.         s++;
  278.  
  279.     /* following technically violates RFC822 */
  280.     while (isspace(*s))
  281.         s++;
  282.  
  283.     return (*s == ':');
  284. }
  285. /*
  286. **  EATHEADER -- run through the stored header and extract info.
  287. **
  288. **    Parameters:
  289. **        e -- the envelope to process.
  290. **
  291. **    Returns:
  292. **        none.
  293. **
  294. **    Side Effects:
  295. **        Sets a bunch of global variables from information
  296. **            in the collected header.
  297. **        Aborts the message if the hop count is exceeded.
  298. */
  299.  
  300. eatheader(e)
  301.     register ENVELOPE *e;
  302. {
  303.     register HDR *h;
  304.     register char *p;
  305.     int hopcnt = 0;
  306.  
  307.     if (tTd(32, 1))
  308.         printf("----- collected header -----\n");
  309.     for (h = e->e_header; h != NULL; h = h->h_link)
  310.     {
  311.         extern char *capitalize();
  312.  
  313.         if (tTd(32, 1))
  314.             printf("%s: %s\n", capitalize(h->h_field), h->h_value);
  315.         /* count the number of times it has been processed */
  316.         if (bitset(H_TRACE, h->h_flags))
  317.             hopcnt++;
  318.  
  319.         /* send to this person if we so desire */
  320.         if (GrabTo && bitset(H_RCPT, h->h_flags) &&
  321.             !bitset(H_DEFAULT, h->h_flags) &&
  322.             (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
  323.         {
  324.             sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
  325.         }
  326.  
  327.         /* log the message-id */
  328. #ifdef LOG
  329.         if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
  330.             strcmp(h->h_field, "message-id") == 0)
  331.         {
  332.             char buf[MAXNAME];
  333.  
  334.             p = h->h_value;
  335.             if (bitset(H_DEFAULT, h->h_flags))
  336.             {
  337.                 expand(p, buf, &buf[sizeof buf], e);
  338.                 p = buf;
  339.             }
  340.             syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
  341.         }
  342. #endif LOG
  343.     }
  344.     if (tTd(32, 1))
  345.         printf("----------------------------\n");
  346.  
  347.     /* store hop count */
  348.     if (hopcnt > e->e_hopcount)
  349.         e->e_hopcount = hopcnt;
  350.  
  351.     /* message priority */
  352.     p = hvalue("precedence");
  353.     if (p != NULL)
  354.         e->e_class = priencode(p);
  355.     if (!QueueRun)
  356.         e->e_msgpriority = e->e_msgsize
  357.                  - e->e_class * WkClassFact
  358.                  + e->e_nrcpts * WkRecipFact;
  359.  
  360.     /* return receipt to */
  361.     p = hvalue("return-receipt-to");
  362.     if (p != NULL)
  363.         e->e_receiptto = p;
  364.  
  365.     /* errors to */
  366.     p = hvalue("errors-to");
  367.     if (p != NULL)
  368.         sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
  369.  
  370.     /* from person */
  371.     if (OpMode == MD_ARPAFTP)
  372.     {
  373.         register struct hdrinfo *hi = HdrInfo;
  374.  
  375.         for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
  376.         {
  377.             if (bitset(H_FROM, hi->hi_flags))
  378.                 p = hvalue(hi->hi_field);
  379.         }
  380.         if (p != NULL)
  381.             setsender(p);
  382.     }
  383.  
  384.     /* full name of from person */
  385.     p = hvalue("full-name");
  386.     if (p != NULL)
  387.         define('x', p, e);
  388.  
  389.     /* date message originated */
  390.     p = hvalue("posted-date");
  391.     if (p == NULL)
  392.         p = hvalue("date");
  393.     if (p != NULL)
  394.     {
  395.         define('a', p, e);
  396.         /* we don't have a good way to do canonical conversion ....
  397.         define('d', newstr(arpatounix(p)), e);
  398.         .... so we will ignore the problem for the time being */
  399.     }
  400.  
  401.     /*
  402.     **  Log collection information.
  403.     */
  404.  
  405. # ifdef LOG
  406.     if (!QueueRun && LogLevel > 1)
  407.     {
  408.         char hbuf[100], *name = hbuf;
  409.  
  410.         if (RealHostName == NULL)
  411.             name = "local";
  412.         else if (RealHostName[0] == '[')
  413.             name = RealHostName;
  414.         else
  415.             (void)sprintf(hbuf, "%.90s (%s)", 
  416.                 RealHostName, inet_ntoa(RealHostAddr.sin_addr));
  417.         syslog(LOG_INFO,
  418.             "%s: from=%s, size=%ld, class=%d, received from %s\n",
  419.             CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
  420.             CurEnv->e_class, name);
  421.     }
  422. # endif LOG
  423. }
  424. /*
  425. **  PRIENCODE -- encode external priority names into internal values.
  426. **
  427. **    Parameters:
  428. **        p -- priority in ascii.
  429. **
  430. **    Returns:
  431. **        priority as a numeric level.
  432. **
  433. **    Side Effects:
  434. **        none.
  435. */
  436.  
  437. priencode(p)
  438.     char *p;
  439. {
  440.     register int i;
  441.  
  442.     for (i = 0; i < NumPriorities; i++)
  443.     {
  444.         if (!strcasecmp(p, Priorities[i].pri_name))
  445.             return (Priorities[i].pri_val);
  446.     }
  447.  
  448.     /* unknown priority */
  449.     return (0);
  450. }
  451. /*
  452. **  CRACKADDR -- parse an address and turn it into a macro
  453. **
  454. **    This doesn't actually parse the address -- it just extracts
  455. **    it and replaces it with "$g".  The parse is totally ad hoc
  456. **    and isn't even guaranteed to leave something syntactically
  457. **    identical to what it started with.  However, it does leave
  458. **    something semantically identical.
  459. **
  460. **    The process is kind of strange.  There are a number of
  461. **    interesting cases:
  462. **        1.  comment <address> comment    ==> comment <$g> comment
  463. **        2.  address            ==> address
  464. **        3.  address (comment)        ==> $g (comment)
  465. **        4.  (comment) address        ==> (comment) $g
  466. **    And then there are the hard cases....
  467. **        5.  add (comment) ress        ==> $g (comment)
  468. **        6.  comment <address (comment)>    ==> comment <$g (comment)>
  469. **        7.    .... etc ....
  470. **
  471. **    Parameters:
  472. **        addr -- the address to be cracked.
  473. **
  474. **    Returns:
  475. **        a pointer to the new version.
  476. **
  477. **    Side Effects:
  478. **        none.
  479. **
  480. **    Warning:
  481. **        The return value is saved in local storage and should
  482. **        be copied if it is to be reused.
  483. */
  484.  
  485. char *
  486. crackaddr(addr)
  487.     register char *addr;
  488. {
  489.     register char *p;
  490.     register int i;
  491.     static char buf[MAXNAME];
  492.     char *rhs;
  493.     bool gotaddr;
  494.     register char *bp;
  495.  
  496.     if (tTd(33, 1))
  497.         printf("crackaddr(%s)\n", addr);
  498.  
  499.     (void) strcpy(buf, "");
  500.     rhs = NULL;
  501.  
  502.     /* strip leading spaces */
  503.     while (*addr != '\0' && isspace(*addr))
  504.         addr++;
  505.  
  506.     /*
  507.     **  See if we have anything in angle brackets.  If so, that is
  508.     **  the address part, and the rest is the comment.
  509.     */
  510.  
  511.     p = index(addr, '<');
  512.     if (p != NULL)
  513.     {
  514.         /* copy the beginning of the addr field to the buffer */
  515.         *p = '\0';
  516.         (void) strcpy(buf, addr);
  517.         (void) strcat(buf, "<");
  518.         *p++ = '<';
  519.  
  520.         /* skip spaces */
  521.         while (isspace(*p))
  522.             p++;
  523.  
  524.         /* find the matching right angle bracket */
  525.         addr = p;
  526.         for (i = 0; *p != '\0'; p++)
  527.         {
  528.             switch (*p)
  529.             {
  530.               case '<':
  531.                 i++;
  532.                 break;
  533.  
  534.               case '>':
  535.                 i--;
  536.                 break;
  537.             }
  538.             if (i < 0)
  539.                 break;
  540.         }
  541.  
  542.         /* p now points to the closing quote (or a null byte) */
  543.         if (*p != '\0')
  544.         {
  545.             /* make rhs point to the extra stuff at the end */
  546.             rhs = p;
  547.             *p++ = '\0';
  548.         }
  549.     }
  550.  
  551.     /*
  552.     **  Now parse the real address part.  "addr" points to the (null
  553.     **  terminated) version of what we are inerested in; rhs points
  554.     **  to the extra stuff at the end of the line, if any.
  555.     */
  556.  
  557.     p = addr;
  558.  
  559.     /* now strip out comments */
  560.     bp = &buf[strlen(buf)];
  561.     gotaddr = FALSE;
  562.     for (; *p != '\0'; p++)
  563.     {
  564.         if (*p == '(')
  565.         {
  566.             /* copy to matching close paren */
  567.             *bp++ = *p++;
  568.             for (i = 0; *p != '\0'; p++)
  569.             {
  570.                 *bp++ = *p;
  571.                 switch (*p)
  572.                 {
  573.                   case '(':
  574.                     i++;
  575.                     break;
  576.  
  577.                   case ')':
  578.                     i--;
  579.                     break;
  580.                 }
  581.                 if (i < 0)
  582.                     break;
  583.             }
  584.             continue;
  585.         }
  586.  
  587.         /*
  588.         **  If this is the first "real" character we have seen,
  589.         **  then we put the "$g" in the buffer now.
  590.         */
  591.  
  592.         if (isspace(*p))
  593.             *bp++ = *p;
  594.         else if (!gotaddr)
  595.         {
  596.             (void) strcpy(bp, "\001g");
  597.             bp += 2;
  598.             gotaddr = TRUE;
  599.         }
  600.     }
  601.  
  602.     /* hack, hack.... strip trailing blanks */
  603.     do
  604.     {
  605.         *bp-- = '\0';
  606.     } while (isspace(*bp));
  607.     bp++;
  608.  
  609.     /* put any right hand side back on */
  610.     if (rhs != NULL)
  611.     {
  612.         *rhs = '>';
  613.         (void) strcpy(bp, rhs);
  614.     }
  615.  
  616.     if (tTd(33, 1))
  617.         printf("crackaddr=>`%s'\n", buf);
  618.  
  619.     return (buf);
  620. }
  621. /*
  622. **  PUTHEADER -- put the header part of a message from the in-core copy
  623. **
  624. **    Parameters:
  625. **        fp -- file to put it on.
  626. **        m -- mailer to use.
  627. **        e -- envelope to use.
  628. **
  629. **    Returns:
  630. **        none.
  631. **
  632. **    Side Effects:
  633. **        none.
  634. */
  635.  
  636. putheader(fp, m, e)
  637.     register FILE *fp;
  638.     register MAILER *m;
  639.     register ENVELOPE *e;
  640. {
  641.     char buf[MAX(MAXFIELD,BUFSIZ)];
  642.     register HDR *h;
  643.     extern char *arpadate();
  644.     extern char *capitalize();
  645.     char obuf[MAX(MAXFIELD,MAXLINE)];
  646.  
  647.     for (h = e->e_header; h != NULL; h = h->h_link)
  648.     {
  649.         register char *p;
  650.         extern bool bitintersect();
  651.  
  652.         if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
  653.             !bitintersect(h->h_mflags, m->m_flags))
  654.             continue;
  655.  
  656.         /* handle Resent-... headers specially */
  657.         if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
  658.             continue;
  659.  
  660.         p = h->h_value;
  661.         if (bitset(H_DEFAULT, h->h_flags))
  662.         {
  663.             /* macro expand value if generated internally */
  664.             expand(p, buf, &buf[sizeof buf], e);
  665.             p = buf;
  666.             if (p == NULL || *p == '\0')
  667.                 continue;
  668.         }
  669.  
  670.         if (bitset(H_FROM|H_RCPT, h->h_flags))
  671.         {
  672.             /* address field */
  673.             bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
  674.  
  675.             if (bitset(H_FROM, h->h_flags))
  676.                 oldstyle = FALSE;
  677.             commaize(h, p, fp, oldstyle, m);
  678.         }
  679.         else
  680.         {
  681.             /* vanilla header line */
  682.             register char *nlp;
  683.  
  684.             (void) sprintf(obuf, "%s: ", capitalize(h->h_field));
  685.             while ((nlp = index(p, '\n')) != NULL)
  686.             {
  687.                 *nlp = '\0';
  688.                 (void) strcat(obuf, p);
  689.                 *nlp = '\n';
  690.                 putline(obuf, fp, m);
  691.                 p = ++nlp;
  692.                 obuf[0] = '\0';
  693.             }
  694.             (void) strcat(obuf, p);
  695.             putline(obuf, fp, m);
  696.         }
  697.     }
  698. }
  699. /*
  700. **  COMMAIZE -- output a header field, making a comma-translated list.
  701. **
  702. **    Parameters:
  703. **        h -- the header field to output.
  704. **        p -- the value to put in it.
  705. **        fp -- file to put it to.
  706. **        oldstyle -- TRUE if this is an old style header.
  707. **        m -- a pointer to the mailer descriptor.  If NULL,
  708. **            don't transform the name at all.
  709. **
  710. **    Returns:
  711. **        none.
  712. **
  713. **    Side Effects:
  714. **        outputs "p" to file "fp".
  715. */
  716.  
  717. commaize(h, p, fp, oldstyle, m)
  718.     register HDR *h;
  719.     register char *p;
  720.     FILE *fp;
  721.     bool oldstyle;
  722.     register MAILER *m;
  723. {
  724.     register char *obp;
  725.     int opos;
  726.     bool firstone = TRUE;
  727.     char obuf[MAXLINE + 3];
  728.  
  729.     /*
  730.     **  Output the address list translated by the
  731.     **  mailer and with commas.
  732.     */
  733.  
  734.     if (tTd(14, 2))
  735.         printf("commaize(%s: %s)\n", h->h_field, p);
  736.  
  737.     obp = obuf;
  738.     (void) sprintf(obp, "%s: ", capitalize(h->h_field));
  739.     opos = strlen(h->h_field) + 2;
  740.     obp += opos;
  741.  
  742.     /*
  743.     **  Run through the list of values.
  744.     */
  745.  
  746.     while (*p != '\0')
  747.     {
  748.         register char *name;
  749.         char savechar;
  750.         extern char *remotename();
  751.         extern char *DelimChar;        /* defined in prescan */
  752.  
  753.         /*
  754.         **  Find the end of the name.  New style names
  755.         **  end with a comma, old style names end with
  756.         **  a space character.  However, spaces do not
  757.         **  necessarily delimit an old-style name -- at
  758.         **  signs mean keep going.
  759.         */
  760.  
  761.         /* find end of name */
  762.         while (isspace(*p) || *p == ',')
  763.             p++;
  764.         name = p;
  765.         for (;;)
  766.         {
  767.             char *oldp;
  768.             char pvpbuf[PSBUFSIZE];
  769.             extern bool isatword();
  770.             extern char **prescan();
  771.  
  772.             (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
  773.             p = DelimChar;
  774.  
  775.             /* look to see if we have an at sign */
  776.             oldp = p;
  777.             while (*p != '\0' && isspace(*p))
  778.                 p++;
  779.  
  780.             if (*p != '@' && !isatword(p))
  781.             {
  782.                 p = oldp;
  783.                 break;
  784.             }
  785.             p += *p == '@' ? 1 : 2;
  786.             while (*p != '\0' && isspace(*p))
  787.                 p++;
  788.         }
  789.         /* at the end of one complete name */
  790.  
  791.         /* strip off trailing white space */
  792.         while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
  793.             p--;
  794.         if (++p == name)
  795.             continue;
  796.         savechar = *p;
  797.         *p = '\0';
  798.  
  799.         /* translate the name to be relative */
  800.         name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
  801.         if (*name == '\0')
  802.         {
  803.             *p = savechar;
  804.             continue;
  805.         }
  806.  
  807.         /* output the name with nice formatting */
  808.         opos += qstrlen(name);
  809.         if (!firstone)
  810.             opos += 2;
  811.         if (opos > 78 && !firstone)
  812.         {
  813.             (void) strcpy(obp, ",\n");
  814.             putline(obuf, fp, m);
  815.             obp = obuf;
  816.             (void) sprintf(obp, "        ");
  817.             opos = strlen(obp);
  818.             obp += opos;
  819.             opos += qstrlen(name);
  820.         }
  821.         else if (!firstone)
  822.         {
  823.             (void) sprintf(obp, ", ");
  824.             obp += 2;
  825.         }
  826.  
  827.         /* strip off quote bits as we output */
  828.         while (*name != '\0' && obp < &obuf[MAXLINE])
  829.         {
  830.             if (bitset(0200, *name))
  831.                 *obp++ = '\\';
  832.             *obp++ = *name++ & ~0200;
  833.         }
  834.         firstone = FALSE;
  835.         *p = savechar;
  836.     }
  837.     (void) strcpy(obp, "\n");
  838.     putline(obuf, fp, m);
  839. }
  840. /*
  841. **  ISATWORD -- tell if the word we are pointing to is "at".
  842. **
  843. **    Parameters:
  844. **        p -- word to check.
  845. **
  846. **    Returns:
  847. **        TRUE -- if p is the word at.
  848. **        FALSE -- otherwise.
  849. **
  850. **    Side Effects:
  851. **        none.
  852. */
  853.  
  854. bool
  855. isatword(p)
  856.     register char *p;
  857. {
  858.     extern char lower();
  859.  
  860.     if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
  861.         p[2] != '\0' && isspace(p[2]))
  862.         return (TRUE);
  863.     return (FALSE);
  864. }
  865.