home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / mail / send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  12.5 KB  |  554 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)send.c    5.23 (Berkeley) 2/9/91";
  36. #endif /* not lint */
  37.  
  38. #include "rcv.h"
  39.  
  40. /*
  41.  * Mail -- a mail program
  42.  *
  43.  * Mail to others.
  44.  */
  45.  
  46. /*
  47.  * Send message described by the passed pointer to the
  48.  * passed output buffer.  Return -1 on error.
  49.  * Adjust the status: field if need be.
  50.  * If doign is given, suppress ignored header fields.
  51.  * prefix is a string to prepend to each output line.
  52.  */
  53. send(mp, obuf, doign, prefix)
  54.     register struct message *mp;
  55.     FILE *obuf;
  56.     struct ignoretab *doign;
  57.     char *prefix;
  58. {
  59.     long count;
  60.     register FILE *ibuf;
  61.     char line[LINESIZE];
  62.     int ishead, infld, ignoring, dostat, firstline;
  63.     register char *cp, *cp2;
  64.     register int c;
  65.     int length;
  66.     int prefixlen;
  67.  
  68.     /*
  69.      * Compute the prefix string, without trailing whitespace
  70.      */
  71.     if (prefix != NOSTR) {
  72.         cp2 = 0;
  73.         for (cp = prefix; *cp; cp++)
  74.             if (*cp != ' ' && *cp != '\t')
  75.                 cp2 = cp;
  76.         prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
  77.     }
  78.     ibuf = setinput(mp);
  79.     count = mp->m_size;
  80.     ishead = 1;
  81.     dostat = doign == 0 || !isign("status", doign);
  82.     infld = 0;
  83.     firstline = 1;
  84.     /*
  85.      * Process headers first
  86.      */
  87.     while (count > 0 && ishead) {
  88.         if (fgets(line, LINESIZE, ibuf) == NULL)
  89.             break;
  90.         count -= length = strlen(line);
  91.         if (firstline) {
  92.             /* 
  93.              * First line is the From line, so no headers
  94.              * there to worry about
  95.              */
  96.             firstline = 0;
  97.             ignoring = doign == ignoreall;
  98.         } else if (line[0] == '\n') {
  99.             /*
  100.              * If line is blank, we've reached end of
  101.              * headers, so force out status: field
  102.              * and note that we are no longer in header
  103.              * fields
  104.              */
  105.             if (dostat) {
  106.                 statusput(mp, obuf, prefix);
  107.                 dostat = 0;
  108.             }
  109.             ishead = 0;
  110.             ignoring = doign == ignoreall;
  111.         } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
  112.             /*
  113.              * If this line is a continuation (via space or tab)
  114.              * of a previous header field, just echo it
  115.              * (unless the field should be ignored).
  116.              * In other words, nothing to do.
  117.              */
  118.         } else {
  119.             /*
  120.              * Pick up the header field if we have one.
  121.              */
  122.             for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
  123.                 ;
  124.             cp2 = --cp;
  125.             while (isspace(*cp++))
  126.                 ;
  127.             if (cp[-1] != ':') {
  128.                 /*
  129.                  * Not a header line, force out status:
  130.                  * This happens in uucp style mail where
  131.                  * there are no headers at all.
  132.                  */
  133.                 if (dostat) {
  134.                     statusput(mp, obuf, prefix);
  135.                     dostat = 0;
  136.                 }
  137.                 if (doign != ignoreall)
  138.                     /* add blank line */
  139.                     (void) putc('\n', obuf);
  140.                 ishead = 0;
  141.                 ignoring = 0;
  142.             } else {
  143.                 /*
  144.                  * If it is an ignored field and
  145.                  * we care about such things, skip it.
  146.                  */
  147.                 *cp2 = 0;    /* temporarily null terminate */
  148.                 if (doign && isign(line, doign))
  149.                     ignoring = 1;
  150.                 else if ((line[0] == 's' || line[0] == 'S') &&
  151.                      strcasecmp(line, "status") == 0) {
  152.                     /*
  153.                      * If the field is "status," go compute
  154.                      * and print the real Status: field
  155.                      */
  156.                     if (dostat) {
  157.                         statusput(mp, obuf, prefix);
  158.                         dostat = 0;
  159.                     }
  160.                     ignoring = 1;
  161.                 } else {
  162.                     ignoring = 0;
  163.                     *cp2 = c;    /* restore */
  164.                 }
  165.                 infld = 1;
  166.             }
  167.         }
  168.         if (!ignoring) {
  169.             /*
  170.              * Strip trailing whitespace from prefix
  171.              * if line is blank.
  172.              */
  173.             if (prefix != NOSTR)
  174.                 if (length > 1)
  175.                     fputs(prefix, obuf);
  176.                 else
  177.                     (void) fwrite(prefix, sizeof *prefix,
  178.                             prefixlen, obuf);
  179.             (void) fwrite(line, sizeof *line, length, obuf);
  180.             if (ferror(obuf))
  181.                 return -1;
  182.         }
  183.     }
  184.     /*
  185.      * Copy out message body
  186.      */
  187.     if (doign == ignoreall)
  188.         count--;        /* skip final blank line */
  189.     if (prefix != NOSTR)
  190.         while (count > 0) {
  191.             if (fgets(line, LINESIZE, ibuf) == NULL) {
  192.                 c = 0;
  193.                 break;
  194.             }
  195.             count -= c = strlen(line);
  196.             /*
  197.              * Strip trailing whitespace from prefix
  198.              * if line is blank.
  199.              */
  200.             if (c > 1)
  201.                 fputs(prefix, obuf);
  202.             else
  203.                 (void) fwrite(prefix, sizeof *prefix,
  204.                         prefixlen, obuf);
  205.             (void) fwrite(line, sizeof *line, c, obuf);
  206.             if (ferror(obuf))
  207.                 return -1;
  208.         }
  209.     else
  210.         while (count > 0) {
  211.             c = count < LINESIZE ? count : LINESIZE;
  212.             if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
  213.                 break;
  214.             count -= c;
  215.             if (fwrite(line, sizeof *line, c, obuf) != c)
  216.                 return -1;
  217.         }
  218.     if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
  219.         /* no final blank line */
  220.         if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
  221.             return -1;
  222.     return 0;
  223. }
  224.  
  225. /*
  226.  * Output a reasonable looking status field.
  227.  */
  228. statusput(mp, obuf, prefix)
  229.     register struct message *mp;
  230.     FILE *obuf;
  231.     char *prefix;
  232. {
  233.     char statout[3];
  234.     register char *cp = statout;
  235.  
  236.     if (mp->m_flag & MREAD)
  237.         *cp++ = 'R';
  238.     if ((mp->m_flag & MNEW) == 0)
  239.         *cp++ = 'O';
  240.     *cp = 0;
  241.     if (statout[0])
  242.         fprintf(obuf, "%sStatus: %s\n",
  243.             prefix == NOSTR ? "" : prefix, statout);
  244. }
  245.  
  246. /*
  247.  * Interface between the argument list and the mail1 routine
  248.  * which does all the dirty work.
  249.  */
  250. mail(to, cc, bcc, smopts, subject)
  251.     struct name *to, *cc, *bcc, *smopts;
  252.     char *subject;
  253. {
  254.     struct header head;
  255.  
  256.     head.h_to = to;
  257.     head.h_subject = subject;
  258.     head.h_cc = cc;
  259.     head.h_bcc = bcc;
  260.     head.h_smopts = smopts;
  261.     mail1(&head, 0);
  262.     return(0);
  263. }
  264.  
  265.  
  266. /*
  267.  * Send mail to a bunch of user names.  The interface is through
  268.  * the mail routine below.
  269.  */
  270. sendmail(str)
  271.     char *str;
  272. {
  273.     struct header head;
  274.  
  275.     head.h_to = extract(str, GTO);
  276.     head.h_subject = NOSTR;
  277.     head.h_cc = NIL;
  278.     head.h_bcc = NIL;
  279.     head.h_smopts = NIL;
  280.     mail1(&head, 0);
  281.     return(0);
  282. }
  283.  
  284. /*
  285.  * Mail a message on standard input to the people indicated
  286.  * in the passed header.  (Internal interface).
  287.  */
  288. mail1(hp, printheaders)
  289.     struct header *hp;
  290. {
  291.     char *cp;
  292.     int pid;
  293.     char **namelist;
  294.     struct name *to;
  295.     FILE *mtf;
  296.  
  297.     /*
  298.      * Collect user's mail from standard input.
  299.      * Get the result as mtf.
  300.      */
  301.     if ((mtf = collect(hp, printheaders)) == NULL)
  302.         return;
  303.     if (value("interactive") != NOSTR)
  304.         if (value("askcc") != NOSTR)
  305.             grabh(hp, GCC);
  306.         else {
  307.             printf("EOT\n");
  308.             (void) fflush(stdout);
  309.         }
  310.     if (fsize(mtf) == 0)
  311.         if (hp->h_subject == NOSTR)
  312.             printf("No message, no subject; hope that's ok\n");
  313.         else
  314.             printf("Null message body; hope that's ok\n");
  315.     /*
  316.      * Now, take the user names from the combined
  317.      * to and cc lists and do all the alias
  318.      * processing.
  319.      */
  320.     senderr = 0;
  321.     to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
  322.     if (to == NIL) {
  323.         printf("No recipients specified\n");
  324.         senderr++;
  325.     }
  326.     /*
  327.      * Look through the recipient list for names with /'s
  328.      * in them which we write to as files directly.
  329.      */
  330.     to = outof(to, mtf, hp);
  331.     if (senderr)
  332.         savedeadletter(mtf);
  333.     to = elide(to);
  334.     if (count(to) == 0)
  335.         goto out;
  336.     fixhead(hp, to);
  337.     if ((mtf = infix(hp, mtf)) == NULL) {
  338.         fprintf(stderr, ". . . message lost, sorry.\n");
  339.         return;
  340.     }
  341.     namelist = unpack(cat(hp->h_smopts, to));
  342.     if (debug) {
  343.         char **t;
  344.  
  345.         printf("Sendmail arguments:");
  346.         for (t = namelist; *t != NOSTR; t++)
  347.             printf(" \"%s\"", *t);
  348.         printf("\n");
  349.         goto out;
  350.     }
  351.     if ((cp = value("record")) != NOSTR)
  352.         (void) savemail(expand(cp), mtf);
  353.     /*
  354.      * Fork, set up the temporary mail file as standard
  355.      * input for "mail", and exec with the user list we generated
  356.      * far above.
  357.      */
  358.     pid = fork();
  359.     if (pid == -1) {
  360.         perror("fork");
  361.         savedeadletter(mtf);
  362.         goto out;
  363.     }
  364.     if (pid == 0) {
  365.         if (access(_PATH_MAIL_LOG, 0) == 0) {
  366.             FILE *postage;
  367.  
  368.             if ((postage = Fopen(_PATH_MAIL_LOG, "a")) != NULL) {
  369.                 fprintf(postage, "%s %d %ld\n", myname,
  370.                     count(to), fsize(mtf));
  371.                 (void) Fclose(postage);
  372.             }
  373.         }
  374.         prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
  375.             sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
  376.             fileno(mtf), -1);
  377.         if ((cp = value("sendmail")) != NOSTR)
  378.             cp = expand(cp);
  379.         else
  380.             cp = _PATH_SENDMAIL;
  381.         execv(cp, namelist);
  382.         perror(cp);
  383.         _exit(1);
  384.     }
  385.     if (value("verbose") != NOSTR)
  386.         (void) wait_child(pid);
  387.     else
  388.         free_child(pid);
  389. out:
  390.     (void) Fclose(mtf);
  391. }
  392.  
  393. /*
  394.  * Fix the header by glopping all of the expanded names from
  395.  * the distribution list into the appropriate fields.
  396.  */
  397. fixhead(hp, tolist)
  398.     struct header *hp;
  399.     struct name *tolist;
  400. {
  401.     register struct name *np;
  402.  
  403.     hp->h_to = NIL;
  404.     hp->h_cc = NIL;
  405.     hp->h_bcc = NIL;
  406.     for (np = tolist; np != NIL; np = np->n_flink)
  407.         if ((np->n_type & GMASK) == GTO)
  408.             hp->h_to =
  409.                 cat(hp->h_to, nalloc(np->n_name, np->n_type));
  410.         else if ((np->n_type & GMASK) == GCC)
  411.             hp->h_cc =
  412.                 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
  413.         else if ((np->n_type & GMASK) == GBCC)
  414.             hp->h_bcc =
  415.                 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
  416. }
  417.  
  418. /*
  419.  * Prepend a header in front of the collected stuff
  420.  * and return the new file.
  421.  */
  422. FILE *
  423. infix(hp, fi)
  424.     struct header *hp;
  425.     FILE *fi;
  426. {
  427.     extern char tempMail[];
  428.     register FILE *nfo, *nfi;
  429.     register int c;
  430.  
  431.     if ((nfo = Fopen(tempMail, "w")) == NULL) {
  432.         perror(tempMail);
  433.         return(fi);
  434.     }
  435.     if ((nfi = Fopen(tempMail, "r")) == NULL) {
  436.         perror(tempMail);
  437.         (void) Fclose(nfo);
  438.         return(fi);
  439.     }
  440.     (void) rm(tempMail);
  441.     (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
  442.     c = getc(fi);
  443.     while (c != EOF) {
  444.         (void) putc(c, nfo);
  445.         c = getc(fi);
  446.     }
  447.     if (ferror(fi)) {
  448.         perror("read");
  449.         rewind(fi);
  450.         return(fi);
  451.     }
  452.     (void) fflush(nfo);
  453.     if (ferror(nfo)) {
  454.         perror(tempMail);
  455.         (void) Fclose(nfo);
  456.         (void) Fclose(nfi);
  457.         rewind(fi);
  458.         return(fi);
  459.     }
  460.     (void) Fclose(nfo);
  461.     (void) Fclose(fi);
  462.     rewind(nfi);
  463.     return(nfi);
  464. }
  465.  
  466. /*
  467.  * Dump the to, subject, cc header on the
  468.  * passed file buffer.
  469.  */
  470. puthead(hp, fo, w)
  471.     struct header *hp;
  472.     FILE *fo;
  473. {
  474.     register int gotcha;
  475.  
  476.     gotcha = 0;
  477.     if (hp->h_to != NIL && w & GTO)
  478.         fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
  479.     if (hp->h_subject != NOSTR && w & GSUBJECT)
  480.         fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
  481.     if (hp->h_cc != NIL && w & GCC)
  482.         fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
  483.     if (hp->h_bcc != NIL && w & GBCC)
  484.         fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
  485.     if (gotcha && w & GNL)
  486.         (void) putc('\n', fo);
  487.     return(0);
  488. }
  489.  
  490. /*
  491.  * Format the given header line to not exceed 72 characters.
  492.  */
  493. fmt(str, np, fo, comma)
  494.     char *str;
  495.     register struct name *np;
  496.     FILE *fo;
  497.     int comma;
  498. {
  499.     register col, len;
  500.  
  501.     comma = comma ? 1 : 0;
  502.     col = strlen(str);
  503.     if (col)
  504.         fputs(str, fo);
  505.     for (; np != NIL; np = np->n_flink) {
  506.         if (np->n_flink == NIL)
  507.             comma = 0;
  508.         len = strlen(np->n_name);
  509.         col++;        /* for the space */
  510.         if (col + len + comma > 72 && col > 4) {
  511.             fputs("\n    ", fo);
  512.             col = 4;
  513.         } else
  514.             putc(' ', fo);
  515.         fputs(np->n_name, fo);
  516.         if (comma)
  517.             putc(',', fo);
  518.         col += len + comma;
  519.     }
  520.     putc('\n', fo);
  521. }
  522.  
  523. /*
  524.  * Save the outgoing mail on the passed file.
  525.  */
  526.  
  527. /*ARGSUSED*/
  528. savemail(name, fi)
  529.     char name[];
  530.     register FILE *fi;
  531. {
  532.     register FILE *fo;
  533.     char buf[BUFSIZ];
  534.     register i;
  535.     time_t now, time();
  536.     char *ctime();
  537.  
  538.     if ((fo = Fopen(name, "a")) == NULL) {
  539.         perror(name);
  540.         return (-1);
  541.     }
  542.     (void) time(&now);
  543.     fprintf(fo, "From %s %s", myname, ctime(&now));
  544.     while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
  545.         (void) fwrite(buf, 1, i, fo);
  546.     (void) putc('\n', fo);
  547.     (void) fflush(fo);
  548.     if (ferror(fo))
  549.         perror(name);
  550.     (void) Fclose(fo);
  551.     rewind(fi);
  552.     return (0);
  553. }
  554.