home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / metamail / tahoe / send.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-02-18  |  11.5 KB  |  576 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 are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific prior written permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifdef notdef
  14. static char sccsid[] = "@(#)send.c    5.5 (Berkeley) 2/18/88";
  15. #endif /* notdef */
  16.  
  17. #include "rcv.h"
  18. #include <sys/wait.h>
  19. #include <sys/stat.h>
  20.  
  21. /*
  22.  * Mail -- a mail program
  23.  *
  24.  * Mail to others.
  25.  */
  26.  
  27. /*
  28.  * Send message described by the passed pointer to the
  29.  * passed output buffer.  Return -1 on error, but normally
  30.  * the number of lines written.  Adjust the status: field
  31.  * if need be.  If doign is set, suppress ignored header fields.
  32.  */
  33. send(mp, obuf, doign)
  34.     register struct message *mp;
  35.     FILE *obuf;
  36. {
  37.     long count;
  38.     register FILE *ibuf;
  39.     char line[LINESIZE];
  40.     int lc, ishead, infld, ignoring, dostat;
  41.     register char *cp, *cp2;
  42.     register int c;
  43.     int length;
  44.  
  45.     ibuf = setinput(mp);
  46.     count = mp->m_size;
  47.     ishead = 1;
  48.     dostat = !doign || !isign("status");
  49.     infld = 0;
  50.     lc = 0;
  51.     /*
  52.      * Process headers first
  53.      */
  54.     while (count > 0 && ishead) {
  55.         if (fgets(line, LINESIZE, ibuf) == NULL)
  56.             break;
  57.         count -= length = strlen(line);
  58.         if (lc == 0) {
  59.             /* 
  60.              * First line is the From line, so no headers
  61.              * there to worry about
  62.              */
  63.             ignoring = 0;
  64.         } else if (line[0] == '\n') {
  65.             /*
  66.              * If line is blank, we've reached end of
  67.              * headers, so force out status: field
  68.              * and note that we are no longer in header
  69.              * fields
  70.              */
  71.             if (dostat) {
  72.                 statusput(mp, obuf);
  73.                 dostat = 0;
  74.             }
  75.             ishead = 0;
  76.             ignoring = 0;
  77.         } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
  78.             /*
  79.              * If this line is a continuation (via space or tab)
  80.              * of a previous header field, just echo it
  81.              * (unless the field should be ignored).
  82.              * In other words, nothing to do.
  83.              */
  84.         } else {
  85.             /*
  86.              * Pick up the header field if we have one.
  87.              */
  88.             for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
  89.                 ;
  90.             cp2 = --cp;
  91.             while (isspace(*cp++))
  92.                 ;
  93.             if (cp[-1] != ':') {
  94.                 /*
  95.                  * Not a header line, force out status:
  96.                  * This happens in uucp style mail where
  97.                  * there are no headers at all.
  98.                  */
  99.                 if (dostat) {
  100.                     statusput(mp, obuf);
  101.                     dostat = 0;
  102.                 }
  103.                 (void) putc('\n', obuf); /* add blank line */
  104.                 lc++;
  105.                 ishead = 0;
  106.                 ignoring = 0;
  107.             } else {
  108.                 /*
  109.                  * If it is an ignored field and
  110.                  * we care about such things, skip it.
  111.                  */
  112.                 *cp2 = 0;    /* temporarily null terminate */
  113.                 if (doign && isign(line))
  114.                     ignoring = 1;
  115.                 else if ((line[0] == 's' || line[0] == 'S') &&
  116.                      icequal(line, "status")) {
  117.                     /*
  118.                      * If the field is "status," go compute
  119.                      * and print the real Status: field
  120.                      */
  121.                     if (dostat) {
  122.                         statusput(mp, obuf);
  123.                         dostat = 0;
  124.                     }
  125.                     ignoring = 1;
  126.                 } else {
  127.                     ignoring = 0;
  128.                     *cp2 = c;    /* restore */
  129.                 }
  130.                 infld = 1;
  131.             }
  132.         }
  133.         if (!ignoring) {
  134.             (void) fwrite(line, sizeof *line, length, obuf);
  135.             if (ferror(obuf))
  136.                 return -1;
  137.             lc++;
  138.         }
  139.     }
  140.     /*
  141.      * Copy out message body
  142.      */
  143.     while (count > 0) {
  144.         cp = line;
  145.         c = count < LINESIZE ? count : LINESIZE;
  146.         if ((c = fread(cp, sizeof *cp, c, ibuf)) <= 0)
  147.             break;
  148.         if (fwrite(cp, sizeof *cp, c, obuf) != c)
  149.             return -1;
  150.         count -= c;
  151.         while (--c >= 0)
  152.             if (*cp++ == '\n')
  153.                 lc++;
  154.     }
  155.     if (ishead && (mp->m_flag & MSTATUS))
  156.         printf("failed to fix up status field\n");
  157.     return (lc);
  158. }
  159.  
  160. /*
  161.  * Output a reasonable looking status field.
  162.  * But if "status" is ignored and doign, forget it.
  163.  */
  164. statusput(mp, obuf)
  165.     register struct message *mp;
  166.     FILE *obuf;
  167. {
  168.     char statout[3];
  169.     register char *cp = statout;
  170.  
  171.     if (mp->m_flag & MREAD)
  172.         *cp++ = 'R';
  173.     if ((mp->m_flag & MNEW) == 0)
  174.         *cp++ = 'O';
  175.     *cp = 0;
  176.     if (statout[0])
  177.         fprintf(obuf, "Status: %s\n", statout);
  178. }
  179.  
  180. /*
  181.  * Interface between the argument list and the mail1 routine
  182.  * which does all the dirty work.
  183.  */
  184.  
  185. mail(people)
  186.     char **people;
  187. {
  188.     register char *cp2;
  189.     register int s;
  190.     char *buf, **ap;
  191.     struct header head;
  192.  
  193.     for (s = 0, ap = people; *ap != 0; ap++)
  194.         s += strlen(*ap) + 1;
  195.     buf = salloc(s+1);
  196.     cp2 = buf;
  197.     for (ap = people; *ap != 0; ap++) {
  198.         cp2 = copy(*ap, cp2);
  199.         *cp2++ = ' ';
  200.     }
  201.     if (cp2 != buf)
  202.         cp2--;
  203.     *cp2 = '\0';
  204.     head.h_to = buf;
  205.     head.h_subject = NOSTR;
  206.     head.h_cc = NOSTR;
  207.     head.h_bcc = NOSTR;
  208.     head.h_seq = 0;
  209.     (void) mail1(&head);
  210.     return(0);
  211. }
  212.  
  213.  
  214. /*
  215.  * Send mail to a bunch of user names.  The interface is through
  216.  * the mail routine below.
  217.  */
  218.  
  219. sendmail(str)
  220.     char *str;
  221. {
  222.     struct header head;
  223.  
  224.     if (blankline(str))
  225.         head.h_to = NOSTR;
  226.     else
  227.         head.h_to = str;
  228.     head.h_subject = NOSTR;
  229.     head.h_cc = NOSTR;
  230.     head.h_bcc = NOSTR;
  231.     head.h_seq = 0;
  232.     (void) mail1(&head);
  233.     return(0);
  234. }
  235.  
  236. /*
  237.  * Mail a message on standard input to the people indicated
  238.  * in the passed header.  (Internal interface).
  239.  */
  240.  
  241. mail1(hp)
  242.     struct header *hp;
  243. {
  244.     register char *cp;
  245.     int pid, i, p, gotcha;
  246.     union wait s;
  247.     char **namelist, *deliver;
  248.     struct name *to, *np;
  249.     struct stat sbuf;
  250.     FILE *mtf, *postage;
  251.     int remote = rflag != NOSTR || rmail;
  252.     char **t;
  253.  
  254.     /*
  255.      * Collect user's mail from standard input.
  256.      * Get the result as mtf.
  257.      */
  258.  
  259.     pid = -1;
  260.     if ((mtf = collect(hp)) == NULL)
  261.         return(-1);
  262.     hp->h_seq = 1;
  263.     if (hp->h_subject == NOSTR)
  264.         hp->h_subject = sflag;
  265.     if (intty && value("askcc") != NOSTR)
  266.         grabh(hp, GCC);
  267.     else if (intty) {
  268.         printf("EOT\n");
  269.         (void) fflush(stdout);
  270.     }
  271.  
  272.     /*
  273.      * Now, take the user names from the combined
  274.      * to and cc lists and do all the alias
  275.      * processing.
  276.      */
  277.  
  278.     senderr = 0;
  279.     to = usermap(cat(extract(hp->h_bcc, GBCC),
  280.         cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
  281.     if (to == NIL) {
  282.         printf("No recipients specified\n");
  283.         goto topdog;
  284.     }
  285.  
  286.     /*
  287.      * Look through the recipient list for names with /'s
  288.      * in them which we write to as files directly.
  289.      */
  290.  
  291.     to = outof(to, mtf, hp);
  292.     rewind(mtf);
  293.     to = verify(to);
  294.     if (senderr && !remote) {
  295. topdog:
  296.  
  297.         if (fsize(mtf) != 0) {
  298.             (void) remove(deadletter);
  299.             (void) exwrite(deadletter, mtf, 1);
  300.             rewind(mtf);
  301.         }
  302.     }
  303.     for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
  304.         if ((np->n_type & GDEL) == 0) {
  305.             gotcha++;
  306.             break;
  307.         }
  308.     if (!gotcha)
  309.         goto out;
  310.     to = elide(to);
  311.     mechk(to);
  312.     if (count(to) > 1)
  313.         hp->h_seq++;
  314.     if (hp->h_seq > 0 && !remote) {
  315.         fixhead(hp, to);
  316.         if (fsize(mtf) == 0)
  317.             if (hp->h_subject == NOSTR)
  318.             printf("No message, no subject; hope that's ok\n");
  319.             else
  320.             printf("Null message body; hope that's ok\n");
  321.         if ((mtf = infix(hp, mtf)) == NULL) {
  322.             fprintf(stderr, ". . . message lost, sorry.\n");
  323.             return(-1);
  324.         }
  325.     }
  326.     namelist = unpack(to);
  327.     if (debug) {
  328.         printf("Recipients of message:\n");
  329.         for (t = namelist; *t != NOSTR; t++)
  330.             printf(" \"%s\"", *t);
  331.         printf("\n");
  332.         (void) fflush(stdout);
  333.         return 0;
  334.     }
  335.     if ((cp = value("record")) != NOSTR)
  336.         (void) savemail(expand(cp), mtf);
  337.  
  338.     /*
  339.      * Wait, to absorb a potential zombie, then
  340.      * fork, set up the temporary mail file as standard
  341.      * input for "mail" and exec with the user list we generated
  342.      * far above. Return the process id to caller in case he
  343.      * wants to await the completion of mail.
  344.      */
  345.  
  346.     while (wait3(&s, WNOHANG, (struct timeval *) 0) > 0)
  347.         ;
  348.     rewind(mtf);
  349.     pid = fork();
  350.     if (pid == -1) {
  351.         perror("fork");
  352.         (void) remove(deadletter);
  353.         (void) exwrite(deadletter, mtf, 1);
  354.         goto out;
  355.     }
  356.     if (pid == 0) {
  357. #ifdef SIGTSTP
  358.         if (remote == 0) {
  359.             (void) signal(SIGTSTP, SIG_IGN);
  360.             (void) signal(SIGTTIN, SIG_IGN);
  361.             (void) signal(SIGTTOU, SIG_IGN);
  362.         }
  363. #endif
  364.         (void) signal(SIGHUP, SIG_IGN);
  365.         (void) signal(SIGINT, SIG_IGN);
  366.         (void) signal(SIGQUIT, SIG_IGN);
  367.         if (!stat(POSTAGE, &sbuf))
  368.             if ((postage = fopen(POSTAGE, "a")) != NULL) {
  369.                 fprintf(postage, "%s %d %ld\n", myname,
  370.                     count(to), fsize(mtf));
  371.                 (void) fclose(postage);
  372.             }
  373.         (void) close(0);
  374.         (void) dup(fileno(mtf));
  375.         for (i = getdtablesize(); --i > 2;)
  376.             (void) close(i);
  377. #ifdef SENDMAIL
  378.         if ((deliver = value("sendmail")) == NOSTR)
  379.             deliver = SENDMAIL;
  380.         execv(deliver, namelist);
  381. #endif SENDMAIL
  382.         execv(MAIL, namelist);
  383.         perror(MAIL);
  384.         exit(1);
  385.     }
  386.  
  387. out:
  388.     if (remote || (value("verbose") != NOSTR)) {
  389.         while ((p = wait(&s)) != pid && p != -1)
  390.             ;
  391.         if (s.w_status != 0)
  392.             senderr++;
  393.         pid = 0;
  394.     }
  395.     (void) fclose(mtf);
  396.     return(pid);
  397. }
  398.  
  399. /*
  400.  * Fix the header by glopping all of the expanded names from
  401.  * the distribution list into the appropriate fields.
  402.  * If there are any ARPA net recipients in the message,
  403.  * we must insert commas, alas.
  404.  */
  405.  
  406. fixhead(hp, tolist)
  407.     struct header *hp;
  408.     struct name *tolist;
  409. {
  410.     register int f;
  411.     register struct name *np;
  412.  
  413.     for (f = 0, np = tolist; np != NIL; np = np->n_flink)
  414.         if (any('@', np->n_name)) {
  415.             f |= GCOMMA;
  416.             break;
  417.         }
  418.  
  419.     if (debug && f & GCOMMA)
  420.         fprintf(stderr, "Should be inserting commas in recip lists\n");
  421.     hp->h_to = detract(tolist, GTO|f);
  422.     hp->h_cc = detract(tolist, GCC|f);
  423. }
  424.  
  425. /*
  426.  * Prepend a header in front of the collected stuff
  427.  * and return the new file.
  428.  */
  429.  
  430. FILE *
  431. infix(hp, fi)
  432.     struct header *hp;
  433.     FILE *fi;
  434. {
  435.     extern char tempMail[];
  436.     register FILE *nfo, *nfi;
  437.     register int c;
  438.  
  439.     rewind(fi);
  440.     if ((nfo = fopen(tempMail, "w")) == NULL) {
  441.         perror(tempMail);
  442.         return(fi);
  443.     }
  444.     if ((nfi = fopen(tempMail, "r")) == NULL) {
  445.         perror(tempMail);
  446.         (void) fclose(nfo);
  447.         return(fi);
  448.     }
  449.     (void) remove(tempMail);
  450.     (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
  451.     c = getc(fi);
  452.     while (c != EOF) {
  453.         (void) putc(c, nfo);
  454.         c = getc(fi);
  455.     }
  456.     if (ferror(fi)) {
  457.         perror("read");
  458.         return(fi);
  459.     }
  460.     (void) fflush(nfo);
  461.     if (ferror(nfo)) {
  462.         perror(tempMail);
  463.         (void) fclose(nfo);
  464.         (void) fclose(nfi);
  465.         return(fi);
  466.     }
  467.     (void) fclose(nfo);
  468.     (void) fclose(fi);
  469.     rewind(nfi);
  470.     return(nfi);
  471. }
  472.  
  473. /*
  474.  * Dump the to, subject, cc header on the
  475.  * passed file buffer.
  476.  */
  477.  
  478. puthead(hp, fo, w)
  479.     struct header *hp;
  480.     FILE *fo;
  481. {
  482.     register int gotcha;
  483.  
  484.     gotcha = 0;
  485.     if (hp->h_to != NOSTR && w & GTO)
  486.         fmt("To: ", hp->h_to, fo), gotcha++;
  487.     if (hp->h_subject != NOSTR && w & GSUBJECT)
  488.         fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
  489.     if (hp->h_cc != NOSTR && w & GCC)
  490.         fmt("Cc: ", hp->h_cc, fo), gotcha++;
  491.     if (hp->h_bcc != NOSTR && w & GBCC)
  492.         fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
  493.     if (gotcha && w & GNL)
  494.         (void) putc('\n', fo);
  495.     return(0);
  496. }
  497.  
  498. /*
  499.  * Format the given text to not exceed 72 characters.
  500.  */
  501.  
  502. fmt(str, txt, fo)
  503.     register char *str, *txt;
  504.     register FILE *fo;
  505. {
  506.     register int col;
  507.     register char *bg, *bl, *pt, ch;
  508.  
  509.     col = strlen(str);
  510.     if (col)
  511.         fprintf(fo, "%s", str);
  512.     pt = bg = txt;
  513.     bl = 0;
  514.     while (*bg) {
  515.         pt++;
  516.         if (++col > 72) {
  517.             if (!bl) {
  518.                 bl = bg;
  519.                 while (*bl && !isspace(*bl))
  520.                     bl++;
  521.             }
  522.             if (!*bl)
  523.                 goto finish;
  524.             ch = *bl;
  525.             *bl = '\0';
  526.             fprintf(fo, "%s\n    ", bg);
  527.             col = 4;
  528.             *bl = ch;
  529.             pt = bg = ++bl;
  530.             bl = 0;
  531.         }
  532.         if (!*pt) {
  533. finish:
  534.             fprintf(fo, "%s\n", bg);
  535.             return;
  536.         }
  537.         if (isspace(*pt))
  538.             bl = pt;
  539.     }
  540. }
  541.  
  542. /*
  543.  * Save the outgoing mail on the passed file.
  544.  */
  545.  
  546. /*ARGSUSED*/
  547. savemail(name, fi)
  548.     char name[];
  549.     register FILE *fi;
  550. {
  551.     register FILE *fo;
  552.     char buf[BUFSIZ];
  553.     register i;
  554.     time_t now, time();
  555.     char *n;
  556.     char *ctime();
  557.  
  558.     if ((fo = fopen(name, "a")) == NULL) {
  559.         perror(name);
  560.         return (-1);
  561.     }
  562.     (void) time(&now);
  563.     if ((n = rflag) == NOSTR)
  564.         n = myname;
  565.     fprintf(fo, "From %s %s", n, ctime(&now));
  566.     rewind(fi);
  567.     while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
  568.         (void) fwrite(buf, 1, i, fo);
  569.     (void) putc('\n', fo);
  570.     (void) fflush(fo);
  571.     if (ferror(fo))
  572.         perror(name);
  573.     (void) fclose(fo);
  574.     return (0);
  575. }
  576.