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 / mail.c.orig < prev    next >
Text File  |  1992-10-30  |  58KB  |  1,889 lines

  1. /* @(#)mail.c     (c) copyright 1986 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. /*
  6.  * mail.c --
  7.  *    do_mail()     external interface to these functions; parses options.
  8.  *    mail_someone()    called from do_mail() to begin composing the message.
  9.  *    start_file()      creates the editing file and reset signal catching.
  10.  *    add_to_letter()    adds the next line to letter --determine ~ escapes.
  11.  *    finish_up_letter()  prompts for Cc:, verifies send, adds signatures.
  12.  *    send_it()     expands aliases, invokes mailer, sends to record file.
  13.  *    add_headers()    adds all headers to a FILE *, reads draft files
  14.  *    rm_edfile()    signals are directed here. remove letter, longjmp
  15.  *    dead_letter()     make a dead.letter if mail failed.
  16.  */
  17.  
  18. static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256];
  19. static int killme;
  20. static u_long flags;
  21. static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)();
  22. static int finish_up_letter(), send_it(), start_file();
  23. static long add_headers();
  24. static jmp_buf cntrl_c_buf;
  25. static char *Hfile, *edfile;
  26. FILE *ed_fp;
  27. char *hfile, *mktemp();
  28.  
  29. /* argc and argv could be null if coming from tool mode compose */
  30. do_mail(n, argv, list)
  31. register int n;   /* no need for "argc", so use the space for a variable */
  32. register char **argv, *list;
  33. {
  34.     char firstchar = (argv)? **argv: 'm';
  35.     char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL;
  36.     char *route = NULL;
  37.     char inc_list[MAXMSGS_BITS], buf[HDRSIZ];
  38.     u_long flgs = 0;
  39.  
  40.     if (ison(glob_flags, IS_GETTING)) {
  41.     wprint("You must finish the letter you are editing first.\n");
  42.     return -1;
  43.     }
  44.     if (ison(glob_flags, DO_PIPE)) {
  45.     wprint("You can't pipe out of the %s command.\n", argv? *argv : "mail");
  46.     return -1;
  47.     }
  48.     clear_msg_list(inc_list);
  49.     hfile = Hfile = NULL;
  50.  
  51.     /* If piped to mail, include the messages piped */
  52.     if (ison(glob_flags, IS_PIPE) ||
  53.     (lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) {
  54.     turnon(flgs, INCLUDE);
  55.     bitput(list, inc_list, msg_cnt, =);
  56.     }
  57.     /* Set SIGN and DO_FORTUNE now so we can turn them off later */
  58.     if (do_set(set_options, "autosign"))
  59.     turnon(flgs, SIGN);
  60.     if (do_set(set_options, "fortune"))
  61.     turnon(flgs, DO_FORTUNE);
  62.     while (argv && *argv && *++argv && **argv == '-') {
  63.     n = 1;
  64.     while (n && argv[0][n])
  65.         switch (argv[0][n]) {
  66. #ifdef VERBOSE_ARG
  67.         case 'v': turnon(flgs, VERBOSE); n++; break;
  68. #endif /* VERBOSE_ARG */
  69.         case 'H':
  70.             if (argv[1]) {
  71.             n = 0;
  72.             Hfile = *++argv;
  73.             } else {
  74.             wprint("Must specify a file\n");
  75.             return -1;
  76.             }
  77.         when 'h':
  78.             if (argv[1]) {
  79.             n = -1; /* it gets incremented below */
  80.             hfile = savestr(*++argv);
  81.             if (ison(glob_flags, REDIRECT)) {
  82.                 turnoff(glob_flags, REDIRECT);
  83.                 turnon(flgs, SEND_NOW);
  84.             }
  85.             } else {
  86.             wprint("Must specify a file containing headers\n");
  87.             return -1;
  88.             }
  89.             /* Fall through */
  90.         case 'E': turnon(flgs, EDIT_HDRS); n++;
  91.         when 'e': turnon(flgs, EDIT); n++;
  92.         when 'F': turnon(flgs, DO_FORTUNE); n++;
  93.         when 'b':
  94.             if (argv[1]) {
  95.             n = 0, bcc = *++argv;
  96.             fix_up_addr(bcc);
  97.             } else {
  98.             wprint("Must specify blind-carbon list\n");
  99.             return -1;
  100.             }
  101.         when 'c':
  102.             if (argv[1]) {
  103.             n = 0, addcc = *++argv;
  104.             fix_up_addr(addcc);
  105.             } else {
  106.             wprint("Must specify carbon-copy list\n");
  107.             return -1;
  108.             }
  109.         when 's':
  110.             if (argv[1])
  111.             n = 0, subj = *++argv;
  112.             else
  113.             n++, turnon(flgs, NEW_SUBJECT);
  114.         when 'i': case 'I': case 'f': {
  115.             int m;
  116.             if (!msg_cnt) {
  117.             wprint("No message to include!\n");
  118.             return -1;
  119.             }
  120.             if (argv[0][n] == 'i') {
  121.             turnon(flgs, INCLUDE);
  122.             turnoff(flgs, INCLUDE_H);
  123.             turnoff(flgs, FORWARD);
  124.             } else if (argv[0][n] == 'I') {
  125.             turnon(flgs, INCLUDE_H);
  126.             turnoff(flgs, INCLUDE);
  127.             turnoff(flgs, FORWARD);
  128.             } else if (argv[0][n] == 'f') {
  129.             turnon(flgs, FORWARD);
  130.             turnon(flgs, SEND_NOW);
  131.             turnoff(flgs, INCLUDE_H);
  132.             turnoff(flgs, INCLUDE);
  133.             }
  134.             /* "-i 3-5" or "-i3-5"  Consider the latter case first */
  135.             if (!argv[0][++n])
  136.             argv++, n = 0;
  137.             (*argv) += n;
  138.             m = get_msg_list(argv, inc_list);
  139.             (*argv) -= n;
  140.             if (m == -1)
  141.             return -1;
  142.             /* if there were args, then go back to the first char
  143.              * in the next argv
  144.              */
  145.             if (m)
  146.             n = 0;
  147.             if (!n) /* n may be 0 from above! */
  148.             argv += (m-1);
  149.         }
  150.         when 'U':
  151.             turnon(flgs, SEND_NOW);
  152.             n++;
  153.         when 'u':
  154.             turnoff(flgs, SIGN);
  155.             turnoff(flgs, DO_FORTUNE);
  156.             n++;
  157.         when 'r':
  158.             if (lower(firstchar) == 'r') {
  159.             route = *++argv;
  160.             n = 0;
  161.             break;
  162.             }
  163.             /* fall thru */
  164.         default:
  165.             if (argv[0][n] != '?') {
  166.             wprint("%c: unknown option\n\n", argv[0][n]);
  167.             return -1;
  168.             } else
  169.             return help(0, "mail", cmd_help);
  170.         }
  171.     }
  172.     if (isoff(flgs, FORWARD)) {
  173.     if (ison(flgs, SEND_NOW)) {
  174.         if (!hfile && !Hfile) {
  175.         wprint("Can't send immediately without draft file.\n");
  176.         return -1;
  177.         }
  178.         turnoff(flgs, EDIT); /* -U overrides -e */
  179.     } else if (do_set(set_options, "autoedit"))
  180.         turnon(flgs, EDIT);
  181.     } else if (ison(flgs, EDIT)) /* -e modifies -f */
  182.     turnoff(flgs, SEND_NOW);
  183. #ifdef VERBOSE_ARG
  184.     if (do_set(set_options, "verbose"))
  185.     turnon(flgs, VERBOSE);
  186. #endif /* VERBOSE_ARG */
  187.     *in_reply_to = *To = *Subject = *Cc = *Bcc = 0;
  188.     if (lower(firstchar) == 'r') {
  189.     char *in_reply_fmt, *pcc = NULL;
  190.     to = To, cc = Cc;
  191.     /*
  192.      * Generate a reply to all the messages passed to respond().  This
  193.      * list is different than the include-msg list above.  Get info about
  194.      * whom the messages were sent to for reply-all.
  195.      * BUG: currently, redundant addresses aren't pruned from Bcc list!
  196.      */
  197.     for (n = 0; n < msg_cnt; n++)
  198.         if (msg_bit(list, n)) {
  199.         if (to != To)
  200.             *to++ = ',', *to++ = ' ';
  201.         (void) reply_to(n, (firstchar == 'R'), buf);
  202.         if (strlen(buf) + (to - To) > sizeof(To) - 1) {
  203.             wprint("# recipients exceeded at msg %d\n", n);
  204.             break;
  205.         }
  206.         to += Strcpy(to, buf);
  207.         if (firstchar == 'R') {
  208.             if (pcc = cc_to(n, buf)) {
  209.             /* if there was a previous cc, append ", " */
  210.             if (cc != Cc)
  211.                 *cc++ = ',', *cc++ = ' ';
  212.             if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1)
  213.                 wprint("# Cc's exceeded at msg %d\n", n);
  214.             else
  215.                 cc += Strcpy(cc, pcc);
  216.             }
  217.         }
  218.         /* remove redundant addresses now, or headers could get too
  219.          * long before the list runs out (it still might)
  220.          */
  221.         rm_redundant_addrs(To, Cc);
  222.         to = To + strlen(To);
  223.         cc = Cc + strlen(Cc);
  224.         }
  225.     /* clean up end of Cc line for replyall's */
  226.     while (*cc == ' ' || *cc == ',')
  227.         *cc-- = '\0';
  228.     if (firstchar == 'R' && !do_set(set_options, "metoo")) {
  229.         /* Each reply_to() call above will leave at least
  230.          * one person in To.  If that one person was us,
  231.          * we need to get removed from the complete list.
  232.          */
  233.         (void) take_me_off(to);
  234.     }
  235.     to = To, cc = Cc;
  236.     if (route || (route = do_set(set_options, "auto_route")))
  237.         /* careful! This routine could add lots-o-bytes and lose addresses
  238.          * to avoid writing out of segment.
  239.          */
  240.         route_addresses(To, Cc, route);
  241.     if (in_reply_fmt = do_set(set_options, "in_reply_to"))
  242.         /* "9" here is a magic # --see compose_hdr() */
  243.         (void) strcpy(in_reply_to,
  244.                 format_hdr(current_msg, in_reply_fmt, FALSE)+9);
  245.     }
  246.     if (ison(flgs, FORWARD) && ison(flgs, EDIT) ||
  247.         lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) {
  248.     turnoff(flgs, NEW_SUBJECT);
  249.     if (subj && *subj && (isoff(flgs, FORWARD) || ison(flgs, EDIT)))
  250.         subj = strcpy(Subject, subj);
  251.     else if (subj = subject_to(current_msg, buf))
  252.         subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r'));
  253.     } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) &&
  254.     (do_set(set_options, "ask") || do_set(set_options, "asksub")))
  255.     turnon(flgs, NEW_SUBJECT);
  256.     if (argv && *argv) {
  257.     char buf[HDRSIZ];
  258.     (void) argv_to_string(buf, argv);
  259.     fix_up_addr(buf);
  260.     to = &To[strlen(To)];
  261.     if (*To)
  262.         *to++ = ',', *to++ = ' ';
  263.     (void) strcpy(to, buf);
  264.     to = To;
  265.     }
  266.     if (addcc && *addcc) {
  267.     cc = &Cc[strlen(Cc)];
  268.     if (*Cc)
  269.         *cc++ = ',', *cc++ = ' ';
  270.     (void) strcpy(cc, addcc); /* addcc has already been fixed up */
  271.     cc = Cc;
  272.     }
  273.     /* remove any redundant addresses that just got added */
  274.     rm_redundant_addrs(To, Cc);
  275.     if (bcc && *bcc)
  276.     (void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */
  277.     bcc = Bcc;
  278.  
  279.     return mail_someone(to, subj, cc, bcc, flgs, inc_list);
  280. }
  281.  
  282. static
  283. mail_someone(to, subject, cc, bcc, flgs, list)
  284. register char *to, *subject, *cc, *bcc, *list;
  285. u_long flgs;
  286. {
  287.     register char *p;
  288.  
  289.     flags = flgs;
  290.     if (to && *to) {
  291.     if (!*To)
  292.         (void) strncpy(To, to, sizeof(To));
  293.     } else
  294.     to = NO_STRING;
  295.     if (subject && *subject) {
  296.     if (!*Subject)
  297.         (void) strncpy(Subject, subject, sizeof(Subject));
  298.     } else
  299.     subject = NO_STRING;
  300.     if (cc && *cc) {
  301.     if (!*Cc)
  302.         (void) strncpy(Cc, cc, sizeof(Cc));
  303.     } else
  304.     Cc[0] = '\0';
  305.     if (bcc && *bcc) {
  306.     if (!*Bcc)
  307.         (void) strncpy(Bcc, bcc, sizeof(Bcc));
  308.     } else
  309.     Bcc[0] = '\0';
  310.  
  311.     if (ison(glob_flags, REDIRECT)) {
  312.     /*
  313.      * NOTE: Could change this to finish_up_letter() to allow
  314.      * signatures to be appended.  The -U! option to mush would
  315.      * be extended to suppress signing when redirection is on.
  316.      */
  317.     int sent = send_it();
  318.     if (sent == -1) {
  319.         wprint("Message not sent!\n");
  320.         rm_edfile(-1);
  321.     }
  322.     return sent;
  323.     }
  324.     /* if (!*to) then prompting will be done */
  325.     if (!istool && !hfile) {
  326.     if (p = set_header("To: ", to, !*to)) {
  327.         if (!*to) /* if user typed To-line here, fix up the addresses */
  328.         fix_up_addr(p);
  329.         (void) strcpy(To, p);
  330.     }
  331.     if (!*To && ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
  332.         turnoff(flags, SEND_NOW); /* user must edit To: line or do again */
  333.         print("(You must add a To: address.)\n");
  334.     }
  335.     /* don't prompt for subject if forwarding mail */
  336.     if (isoff(flags, FORWARD) && (*subject || ison(flags, NEW_SUBJECT)) &&
  337.         (p = set_header("Subject: ", subject,
  338.             !*subject && ison(flags, NEW_SUBJECT))))
  339.         (void) strcpy(Subject, p);
  340.     if (*Cc || ison(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
  341.         if ((p = set_header("Cc: ", cc, !*Cc)) && *p) {
  342.         fix_up_addr(p);
  343.         (void) strcpy(Cc, p);
  344.         }
  345.     }
  346.     if (*Bcc)
  347.         print("Bcc: %s\n", Bcc);
  348.     putchar('\n');
  349.     }
  350.  
  351.     /* If forwarding w/o editing, start a new file for each. */
  352.     if (ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
  353.     char fwd[MAXMSGS_BITS];
  354.     register int i;
  355.     clear_msg_list(fwd);
  356.     for (i = 0; i < msg_cnt; i++)
  357.         if (msg_bit(list, i)) {
  358.         set_msg_bit(fwd, i);
  359.         if (start_file(fwd) < 0)
  360.             return -1;
  361.         turnon(msg[i].m_flags, FORWARD);
  362.         if (isoff(glob_flags, READ_ONLY))
  363.             turnon(glob_flags, DO_UPDATE);
  364.         clear_msg_list(fwd);
  365.         }
  366.     } else
  367.     return start_file(list);
  368.     return 0;
  369. }
  370.  
  371. static
  372. start_file(list)
  373. char *list;
  374. {
  375.     register char  *dir;
  376.     register int   i;
  377.     char         line[MAXPATHLEN];
  378.     int           had_hfile = FALSE;
  379.  
  380.     /* getdir() uses the home directory if no tmpdir */
  381.     if (!(dir = getdir(do_set(set_options, "tmpdir"))))
  382. alted:
  383.     dir = ALTERNATE_HOME;
  384.     (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE));
  385.     strdup(edfile, line);
  386.     if (!(ed_fp = mask_fopen(edfile, "w+"))) {
  387.     if (strcmp(dir, ALTERNATE_HOME))
  388.         goto alted;
  389.     error("can't create %s", edfile);
  390.     return -1;
  391.     }
  392.     if (!istool) {
  393.     oldint = signal(SIGINT, rm_edfile);
  394.     oldquit = signal(SIGQUIT, rm_edfile);
  395.     oldterm = signal(SIGTERM, rm_edfile);
  396.     }
  397.  
  398.     if (istool && isoff(flags, SEND_NOW) ||
  399.         (isoff(flags, FORWARD) || isoff(flags, SEND_NOW)) &&
  400.         (ison(flags, EDIT_HDRS) || do_set(set_options, "edit_hdrs"))) {
  401.     turnon(flags, EDIT_HDRS);
  402.     if (hfile)
  403.         had_hfile = TRUE;
  404.     if (add_headers(NULL_FILE, &ed_fp, 1, flags) == (long) -1)
  405.         return -1;
  406.     }
  407.     if (Hfile) {
  408.     (void) file_to_fp(Hfile, ed_fp, "r");
  409.     Hfile = NULL;
  410.     had_hfile = TRUE;
  411.     }
  412.     if (istool && isoff(flags, SEND_NOW))
  413.     strdup(hfile, edfile);
  414.  
  415.     /* if flags call for it, include current message (with header?) */
  416.     if (ison(flags, INCLUDE|FORWARD|INCLUDE_H)) {
  417.     long copy_flgs = 0, is_forw = ison(flags, FORWARD);
  418.     char buf[sizeof(To)];
  419.     if (!is_forw) {
  420.         turnon(copy_flgs, INDENT);
  421.         if (ison(flags, INCLUDE_H) &&
  422.             !chk_option("alwaysignore", "include"))
  423.         turnon(copy_flgs, NO_IGNORE);
  424.         else if (ison(flags, INCLUDE))
  425.         turnon(copy_flgs, NO_HEADER);
  426.     } else if (ison(flags, SEND_NOW) ||
  427.         !chk_option("alwaysignore", "forward"))
  428.         turnon(copy_flgs, FORWARD);    /* FORWARD implies NO_IGNORE */
  429. #ifdef MSG_SEPARATOR
  430.     turnon(copy_flgs, NO_SEPARATOR);
  431. #endif /* MSG_SEPARATOR */
  432.     for (i = 0; i < msg_cnt; i++)
  433.         if (msg_bit(list, i)) {
  434.         if (is_forw && isoff(flags, SEND_NOW)) {
  435.             (void) reply_to(i, FALSE, buf);
  436.             (void) fprintf(ed_fp,"--- Forwarded mail from %s\n\n",buf);
  437.         }
  438.         wprint("%sing message %d ...",
  439.             is_forw? "forward" : "includ", i+1);
  440.         wprint("(%d lines)\n",
  441.             copy_msg(i, ed_fp, (u_long) copy_flgs, NULL));
  442.         set_isread(i); /* if we included it, we read it, right? */
  443.         if (is_forw && isoff(flags, SEND_NOW))
  444.             (void) fprintf(ed_fp,
  445.             "\n--- End of forwarded message from %s\n", buf);
  446.         if (!is_forw || isoff(flags, SEND_NOW))
  447.             (void) fputc('\n', ed_fp);
  448.         }
  449.     (void) fflush(ed_fp);
  450.     }
  451.     if (!istool && ison(glob_flags, WARNING)) {
  452.     if (escape && strncmp(escape, DEF_ESCAPE, 1))
  453.         print("(escape character is set to `%c')\n", *escape);
  454.     if (wrapcolumn && wrapcolumn < 20)
  455.         print("(warning: wrapping only %d columns from the left!)\n",
  456.             wrapcolumn);
  457.     }
  458.  
  459.     /* do an "if" again in case editor not found and EDIT turned off */
  460.     if (!istool && ison(flags, EDIT)) {
  461.     char **argv, *edit;
  462.     int argc;
  463.     if ((!(edit = do_set(set_options, "visual")) || !*edit) &&
  464.         (!(edit = do_set(set_options, "editor")) || !*edit))
  465.         edit = DEF_EDITOR;
  466.     (void) sprintf(line, "%s %s", edit, edfile);
  467.     if ((argv = mk_argv(line, &argc, FALSE)) && argc > 0) {
  468.         print("Starting \"%s\"...\n", argv[0]);
  469.         (void) fclose(ed_fp);
  470.         ed_fp = NULL_FILE;
  471.         execute(argv);
  472.         free_vec(argv);
  473.         turnoff(flags, EDIT);
  474.         turnoff(flags, FORWARD); /* forwarded messages must be unedited */
  475.         /* upon exit of editor, user must now type eofc or "." to send */
  476.         if (!(ed_fp = fopen(edfile, "r+"))) {
  477.         error("can't reopen %s", edfile);
  478.         return -1;
  479.         }
  480.         (void) fseek(ed_fp, 0L, 2);
  481.     } else
  482.         print("Unable to start \"%s\"\n", edit);
  483.     wprint("(continue editing letter or ^%c to send)\n", eofc + '@');
  484.     } else if (ison(flags, SEND_NOW)) {
  485.     /* if finish_up_letter() was successful, file was successfully sent. */
  486.     if (!setjmp(cntrl_c_buf) && finish_up_letter() == 0) {
  487.         rm_edfile(0);
  488.         return 0;
  489.     }
  490.     } else if (had_hfile) {
  491.     /* it's not obvious what's going on -- enlighten user */
  492.     wprint("(continue editing or ^%c to send)\n", eofc + '@');
  493.     }
  494.  
  495. #ifdef SUNTOOL
  496.     if (istool) {
  497.     /* toolmode doesn't care if SEND_NOW -- user continues to edit file.
  498.      * if SEND_NOW is not set, then the editor file has just been started,
  499.      * so again, just return so user can edit file.
  500.      */
  501.     if (ed_fp)
  502.         fclose(ed_fp), ed_fp = NULL_FILE;
  503.     turnon(glob_flags, IS_GETTING);
  504.     return 0;
  505.     }
  506. #endif /* SUNTOOL */
  507.     if (ison(flags, SEND_NOW)) {
  508.     /* editing couldn't have been on -- finish_up_letter() failed */
  509.     rm_edfile(0 - ison(flags, FORWARD));
  510.     return -1;
  511.     }
  512.  
  513.     i = 0;
  514.     turnon(glob_flags, IS_GETTING);
  515.     do  {
  516.     /* If the user hits ^C in cbreak mode, mush will return to
  517.      * Getstr and not clear the buffer. whatever is typed next will
  518.      * be appended to the line.  jumping here will force the line to
  519.      * be cleared cuz it's a new call.
  520.      */
  521.     (void) setjmp(cntrl_c_buf);
  522.     while (Getstr(line, sizeof(line), 0) > -1) {
  523.         if (!istool) /* toolmode checks on a timer -- don't do it here */
  524.         (void) check_new_mail(); /* if new mail comes in, get it */
  525.         if ((i = add_to_letter(line)) <= 0)
  526.         break;
  527.     }
  528.     } while (i >= 0 && finish_up_letter() == -1);
  529.     turnoff(glob_flags, IS_GETTING);
  530.     return i; /* return -1 if ~x or ~q to terminate letter */
  531. }
  532.  
  533. char *tilde_commands[] = {
  534.     "commands: [OPTIONAL argument]",
  535.     "t [list]\tChange list of recipients",
  536.     "s [subject]\tModify [set] subject header",
  537.     "c [cc list]\tModify [set] carbon copy recipients",
  538.     "b [bcc list]\tModify [set] blind carbon recipients",
  539.     "h\t\tModify all message headers",
  540.     "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi",
  541.     "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi",
  542.     "u\t\tEdit previous (last) line in file.",
  543.     "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more",
  544.     "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"",
  545.     "I [msg#'s]\tSame, but include the message headers from included messages",
  546.     "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"",
  547.     "S[!]\t\tInclude Signature file [suppress file]",
  548.     "F[!]\t\tAdd a fortune at end of letter [don't add]",
  549.     "w file\t\tWrite msg buffer to file name",
  550.     "a file\t\tAppend msg buffer to file name",
  551.     "r file\t\tRead filename into message buffer",
  552.     "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).",
  553.     "x \t\tQuit message; don't save in dead.letter.",
  554.     "$variable\tInsert the string value for \"variable\" into message.",
  555.     ":cmd\t\tRun the mail command \"cmd\".",
  556.     "|cmd\t\tPipe the message through the unix command \"cmd\".",
  557.     "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].",
  558.     0
  559. };
  560.  
  561. /*
  562.  * TC_EDIT(tc) returns TRUE if tilde_command[tc] involves message
  563.  * editing operations.  Used when EDIT_HDRS is active.
  564.  */
  565. #define TC_EDIT(tc) ((tc) && ((tc) < 6 || !tilde_commands[(tc)+1]))
  566.  
  567. /*
  568.  * Add the line (char *) parameter to the letter.  Determine tilde
  569.  * escapes and determine what to do.  This function returns 0 to
  570.  * indicate user wants to end the letter, -1 if the letter cannot
  571.  * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate
  572.  * successful addition of the line to the letter.
  573.  * This function may be called by toolmode just to change certain mush
  574.  * internal variables via tilde escapes.  Thus, ed_fp might be null.
  575.  */
  576. add_to_letter(line)
  577. char line[];
  578. {
  579.     register char *p;
  580.     char buf[HDRSIZ > MAXPATHLEN ? HDRSIZ : MAXPATHLEN];
  581.  
  582.     killme = 0;
  583.     if (ed_fp) /* may be null if istool */
  584.     (void) fseek(ed_fp, 0L, 2);
  585.  
  586.     if (!strcmp(line, ".") && do_set(set_options, "dot"))
  587.     return 0;
  588.     if (line[0] != *escape || ison(glob_flags, QUOTE_MACRO)) {
  589.     (void) fputs(line, ed_fp);
  590.     (void) fputc('\n', ed_fp);
  591.     (void) fflush(ed_fp);
  592.     return 1;
  593.     }
  594.     /* all commands are "~c" (where 'c' is the command). set p = first
  595.      * character after 'c' and skip whitespace
  596.      */
  597.     p = &line[2];
  598.     skipspaces(0);
  599.     switch (line[1]) {
  600.     case 'v' : case 'p': case 'e' : case '|' : {
  601.         if (!*p || *p == 'i')
  602.         switch (line[1]) {
  603.             case 'p' :
  604.             if (!*p && !(p = do_set(set_options, "pager")))
  605.                 p = DEF_PAGER;
  606.             if (!*p || !strcmp(p, "internal"))
  607.                 p = NULL;
  608.             when 'v' :
  609.             if (*p && p[1] || (p = do_set(set_options, "visual")))
  610.                 break;
  611.             /* else fall through */
  612.             default :
  613.             if (!(p = do_set(set_options, "editor")) || !*p)
  614.                 p = DEF_EDITOR;
  615.             when '|' :
  616.             print("No command for pipe\n");
  617.             return 1;
  618.         }
  619.         if (line[1] == 'p' || line[1] == '|')
  620.         rewind(ed_fp);
  621.         if (line[1] == 'p') {
  622.         (void) do_pager(p, TRUE); /* start the pager "p" */
  623.         if (isoff(flags, EDIT_HDRS)) {
  624.             (void) do_pager(sprintf(buf, "To: %s\n", To), FALSE);
  625.             if (Subject[0])
  626.             (void) do_pager(sprintf(buf, "Subject: %s\n", Subject),
  627.                     FALSE);
  628.             if (Cc[0])
  629.             (void) do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE);
  630.             if (Bcc[0])
  631.             (void) do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE);
  632.             (void) do_pager(strcpy(buf,
  633.                         "--------\nMessage contains:\n"),
  634.             FALSE);
  635.         }
  636.         while (fgets(buf, sizeof(buf), ed_fp))
  637.             if (do_pager(buf, FALSE) == EOF)
  638.             break;
  639.         (void) do_pager(NULL, FALSE); /* end pager */
  640.         } else if (line[1] == '|') {
  641.         FILE *pipe_fp;
  642.         (void) sprintf(buf, "( %s ) > %s", p, edfile);
  643.         if (unlink(edfile) < 0) {
  644.             error("Can't unlink %s:", edfile);
  645.             break; /* Drop out of switch */
  646.         }
  647.         if ((pipe_fp = popen(buf, "w")) == NULL_FILE) {
  648.             error("Can't run \"%s\":", p);
  649.             (void) file_to_fp(edfile, ed_fp, "w");
  650.         } else {
  651.             while (fgets(buf, sizeof(buf), ed_fp))
  652.             if (fputs(buf, pipe_fp) == EOF) {
  653.                 print("Broken pipe\n");
  654.                 break;
  655.             }
  656.             (void) pclose(pipe_fp);
  657.         }
  658.         pipe_fp = ed_fp; /* save ed_fp until we can reopen it */
  659.         if (!(ed_fp = fopen(edfile, "r+"))) {
  660.             error("can't reopen %s", edfile);
  661.             (void) rewind(pipe_fp);
  662.             if (file_to_fp(edfile, pipe_fp, "w") < 0 ||
  663.                 !(ed_fp = fopen(edfile, "r+"))) {
  664.             error("can't restore old contents of %s", edfile);
  665.             ed_fp = pipe_fp;
  666.             dead_letter(0);
  667.             return -1;
  668.             }
  669.         }
  670.         (void) fclose(pipe_fp);
  671.         } else {
  672.         char **argv;
  673.         int argc;
  674.         (void) sprintf(buf, "%s %s", p, edfile);
  675.         if ((argv = mk_argv(buf, &argc, FALSE)) && argc > 0) {
  676.             (void) fclose(ed_fp);
  677.             ed_fp = NULL_FILE;
  678.             execute(argv);
  679.             free_vec(argv);
  680.             /* tool will return even tho editor isn't done */
  681.             if (!(ed_fp = fopen(edfile, "r+"))) {
  682.             error("can't reopen %s", edfile);
  683.             return -1;
  684.             }
  685.         } else
  686.             print("Unable to start \"%s\"\n", p);
  687.         }
  688.     }
  689.     when '$': {
  690.         register char *p2;
  691.         if (!(p2 = do_set(set_options, p)))
  692.         print("(%s isn't set)\n", p);
  693.         else
  694.         putstring(p2, ed_fp);
  695.     }
  696.     when ':': {
  697.         char new[MAXMSGS_BITS];
  698.         u_long save_flags = glob_flags;
  699.  
  700.         turnon(glob_flags, IGN_SIGS);
  701.         turnoff(glob_flags, DO_PIPE);
  702.         turnoff(glob_flags, IS_PIPE);
  703.         (void) cmd_line(p, new);
  704.         glob_flags = save_flags;
  705.     }
  706.     when 'i': case 'f': case 'I': case 'm': {
  707.         int  n;
  708.         u_long copy_flgs = 0;
  709.         char list[MAXMSGS_BITS];
  710.  
  711.         if (!msg_cnt) {
  712.         wprint("No messages.\n");
  713.         break;
  714.         }
  715.         clear_msg_list(list);
  716.         if (line[1] != 'f') {
  717.         turnon(copy_flgs, INDENT);
  718.         if (line[1] == 'i')
  719.             turnon(copy_flgs, NO_HEADER);
  720.         else if (!chk_option("alwaysignore", "include"))
  721.             turnon(copy_flgs, NO_IGNORE);
  722.          } else if (!chk_option("alwaysignore", "forward"))
  723.         turnon(copy_flgs, NO_IGNORE);
  724. #ifdef MSG_SEPARATOR
  725.         turnon(copy_flgs, NO_SEPARATOR);
  726. #endif /* MSG_SEPARATOR */
  727.         if (!*p)
  728.         set_msg_bit(list, current_msg);
  729.         else if (!do_range(p, list))
  730.         return 1;
  731.         for (n = 0; n < msg_cnt; n++)
  732.         if (msg_bit(list, n)) {
  733.             if (line[1] == 'f') {
  734.             (void) reply_to(n, FALSE, buf);
  735.             (void) fprintf(ed_fp,
  736.                     "--- Forwarded mail from %s\n\n", buf);
  737.             }
  738.             wprint("Including message %d ... ", n+1);
  739.             wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs, NULL));
  740.             set_isread(n);
  741.             if (line[1] == 'f')
  742.             (void) fprintf(ed_fp,
  743.                 "\n--- End of forwarded message from %s\n\n", buf);
  744.             else
  745.             (void) fputc('\n', ed_fp);
  746.         }
  747.     }
  748.     /* To: Cc: and Bcc: headers */
  749.     when 'b':
  750.     case 't':
  751.     case 'c': {
  752.         char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc;
  753.         char *Prompt = line[1] == 't'? "To: " :
  754.                line[1] == 'c'? "Cc: " : "Bcc: ";
  755.         if (ison(flags, EDIT_HDRS)) {
  756.         print("You must use an editor to change your headers.\n");
  757.         break;
  758.         }
  759.  
  760.         if (*p) {
  761.         fix_up_addr(p);
  762.         if (*h)
  763.             (void) sprintf(h+strlen(h), ", %s", p);
  764.         else
  765.             (void) strcpy(h, p);
  766.         } else if (!(p = set_header(Prompt, h, TRUE)) || !*p)
  767.         *h = 0;
  768.         else {
  769.         fix_up_addr(p);
  770.         (void) strcpy(h, p);
  771.         }
  772.     }
  773.     when 's':
  774.         if (ison(flags, EDIT_HDRS)) {
  775.         print("You must use an editor to change your headers.\n");
  776.         break;
  777.         }
  778.         if (*p || (p = set_header("Subject: ", Subject, 1)))
  779.         if (!*p)
  780.             Subject[0] = 0;
  781.         else
  782.             (void) strcpy(Subject, p);
  783.     when 'h':
  784.         if (ison(flags, EDIT_HDRS)) {
  785.         print("You must use an editor to change your headers.\n");
  786.         break;
  787.         }
  788.         while ((p = set_header("To: ", To, 1)) && !*p)
  789.         print("(There must be a recipient.)\n");
  790.         (void) strcpy(To, p);
  791.         if (p = set_header("Subject: ", Subject, 1))
  792.         if (!*p)
  793.             Subject[0] = 0;
  794.         else
  795.             (void) strcpy(Subject, p);
  796.         if (p = set_header("Cc: ", Cc, 1))
  797.         if (!*p)
  798.             Cc[0] = 0;
  799.         else {
  800.             fix_up_addr(p);
  801.             (void) strcpy(Cc, p);
  802.         }
  803.         if (p = set_header("Bcc: ", Bcc, 1))
  804.         if (!*p)
  805.             Bcc[0] = 0;
  806.         else {
  807.             fix_up_addr(p);
  808.             (void) strcpy(Bcc, p);
  809.         }
  810.     when 'F': case 'S' : {
  811.         if (*p == '!')
  812.         turnoff(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
  813.         else
  814.         turnon(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
  815.         wprint("%sadding %s at end of message.\n", *p == '!'? "not " : "",
  816.         line[1] == 'F'? "fortune" : "signature");
  817.     }
  818.     when 'w': case 'a': case 'r':
  819.         if (!*p) {
  820.         print("(you must specify a filename)\n");
  821.         return 1;
  822.         }
  823.         (void) fseek(ed_fp, 0L, 2); /* append */
  824.         (void) file_to_fp(p, ed_fp, (line[1] == 'r')? "r":
  825.                   (line[1] == 'w')? "w": "a");
  826.     /* go up one line in the message file and allow the user to edit it */
  827.     when 'u': {
  828.         long newpos, pos = ftell(ed_fp);
  829.         char oldline[256];
  830.         if (pos <= 0L) { /* pos could be -1 if ftell() failed */
  831.         print("(No previous line in file.)\n");
  832.         return 1;
  833.         }
  834.         /* get the last 256 bytes written and read backwards from the
  835.          * current place until '\n' is found. Start by moving past the
  836.          * first \n which is at the end of the line we want to edit
  837.          */
  838.         newpos = max(0, pos - 256L);
  839.         (void) fseek(ed_fp, newpos, L_SET);
  840.         /* don't fgets -- it'll stop at a \n */
  841.         (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp);
  842.         pos--;
  843.         /* the last char in line should be a \n cuz it was last input */
  844.         if (line[(int)(pos-newpos)] != '\n')
  845.         print("I don't know how, but your last line ended with %c.\n",
  846.             line[(int)(pos-newpos)]);
  847.         else
  848.         line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */
  849.         for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--)
  850.         ;
  851.         /* we've gone back to the end of the second previous line. Check
  852.          * to see if the char we're pointing to is a \n.  It should be, but
  853.          * if it's not, we moved back to the first line of the file.
  854.          */
  855.         if (line[(int)(pos-newpos)] == '\n')
  856.         ++pos;
  857.         /* save the old line that's there in case the user boo-boos */
  858.         (void) strcpy(oldline, &line[(int)(pos-newpos)]);
  859.         /* let set header print out the line and get the input */
  860.         if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) {
  861.         print("Something bad happened and I don't know what it is.\n");
  862.         p = oldline;
  863.         } else if (*p == *escape)
  864.         print("(Warning: %c escapes ignored on %cu lines.)\n",
  865.                 *escape, *escape);
  866.         /* seek to to the position where the new line will go */
  867.         (void) fseek(ed_fp, pos, L_SET);
  868.         /* put the newly typed line */
  869.         (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */
  870.         /* if the new line is less than the old line, we're going to do
  871.          * one of two things.  The best thing to do is to truncate the
  872.          * file to the end of the new line.  Sys-v can't do that, so we
  873.          * pad the line with blanks.  May be messy in some cases, but...
  874.          */
  875.         if ((pos = strlen(p) - strlen(oldline)) < 0) {
  876. #ifndef SYSV
  877.         /* add the \n, flush the file, truncate to the current pos */
  878.         (void) fputc('\n', ed_fp);
  879.         (void) fflush(ed_fp);
  880.         (void) ftruncate(fileno(ed_fp), (off_t) ftell(ed_fp));
  881. #else /* SYSV */
  882.         /* pad with blanks to the length of the old line. add \n */
  883.         while (pos++ < 0)
  884.             (void) fputc(' ', ed_fp);
  885.         (void) fputc('\n', ed_fp), (void) fflush(ed_fp);
  886. #endif /* SYSV */
  887.         } else {
  888.         /* the new line is >= the old line, add \n -- no trunc req. */
  889.             (void) fputc('\n', ed_fp);
  890.         (void) fflush(ed_fp);
  891.         }
  892.         return 1;
  893.      }
  894.     /* break;  not here cuz of "return" (lint). */
  895.     case 'E':
  896.         if (ison(flags, EDIT_HDRS)) {
  897.         print("You must use an editor to empty the message buffer.\n");
  898.         break;
  899.         }
  900.         if (*p != '!' && !do_set(set_options, "nosave"))
  901.         dead_letter(0);
  902.         if (emptyfile(&ed_fp, edfile) == -1) {
  903.         error(edfile);
  904.         return -1;
  905.         } else
  906.         print("Message buffer empty\n");
  907.     when 'q':
  908.         /* save in dead.letter if nosave not set -- rm_edfile(-2). */
  909.         rm_edfile(-2); /* doesn't return out of tool mode */
  910.         return -1;
  911.         /* break; not stated cuz of "return" (lint) */
  912.     case 'x':
  913.         /* don't save dead.letter -- simulate normal rm_edfile() call */
  914.         rm_edfile(0);
  915.         return -1;
  916.         /* break; (not specified for lint) */
  917.     default:
  918.         if (line[1] == *escape) {
  919.         (void) fputs(&line[1], ed_fp);
  920.         (void) fputc('\n', ed_fp);
  921.         (void) fflush(ed_fp);
  922.         return 1;
  923.         } else if (line[1] == '?') {
  924.         register int x;
  925.         (void) do_pager(NULL, TRUE); /* start pager */
  926.         for (x = 0; tilde_commands[x]; x++) {
  927.             if (ison(flags, EDIT_HDRS) && TC_EDIT(x))
  928.             continue;
  929.             (void) sprintf(buf, "%s%s\n", escape, tilde_commands[x]);
  930.             if (do_pager(buf, FALSE) == EOF)
  931.             break;
  932.         }
  933.         if (tilde_commands[x] == NULL) {
  934.             (void) sprintf(buf,
  935.             "%s%s\t\tBegin a line with a single %s\n",
  936.             escape, escape, escape);
  937.             (void) do_pager(buf, FALSE);
  938.         }
  939.         (void) do_pager(NULL, FALSE); /* end pager */
  940.         } else
  941.         print("`%c': unknown %c escape. Use %c? for help.\n",
  942.             line[1], *escape, *escape);
  943.         return 1;
  944.     }
  945.     if (ed_fp)
  946.     (void) fseek(ed_fp, 0L, 2);
  947.     if (!istool)
  948.     wprint("(continue editing letter)\n");
  949.     return 1;
  950. }
  951.  
  952. /*
  953.  * finish up the letter. ask for the cc line, if verify is set, ask to
  954.  * verify sending, continue editing, or to dump the whole idea.
  955.  * Then check for signature and fortune.  Finally, pass it to send_it()
  956.  * to actually send it off.
  957.  * Return 0 on success, -1 on failure.
  958.  */
  959. static int
  960. finish_up_letter()
  961. {
  962.     register char *p;
  963.     int c;
  964.     char buf[MAXPATHLEN];
  965.  
  966.     /* forwarded mail has no additional personalized text */
  967.     if (ison(flags, FORWARD)) {
  968.     if (send_it() == -1) {
  969.         wprint("Message not sent!\n");
  970.         return -1;
  971.     }
  972.     turnoff(glob_flags, IS_GETTING);
  973.     return 0;
  974.     }
  975.  
  976.     /* REDIRECT should never be on here, but just in case */
  977.     if (isoff(glob_flags, REDIRECT)) {
  978.     if (!istool) {
  979.         if (isoff(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
  980.         if (p = set_header("Cc: ", Cc, 1))
  981.             (void) strcpy(Cc, p);
  982.         }
  983.     }
  984.     /* ~v on the Cc line asks for verification, first initialize p! */
  985.     p = NULL;
  986.     if (!strncmp(Cc, "~v", 2) ||
  987.         /* Flashy test for $verify either empty or set to "mail" */
  988.         glob(p = do_set(set_options, "verify"),
  989.                         "{,{,*[ \\,]}mail{,[ \\,]*}}")) {
  990.         if (!p) /* so we don't Cc to ~v! */
  991.         *Cc = 0;
  992.         for (;;) {
  993. #ifdef SUNTOOL
  994.         if (istool)
  995.             c = (ask("Send Message?") == TRUE)? 's' : 'c';
  996.         else
  997. #endif /* SUNTOOL */
  998.         {
  999.             print("send, continue editing, discard [s,c,d]? ");
  1000.             c = Getstr(buf, sizeof(buf), 0);
  1001.             if (c < 0)
  1002.             putchar('\n');
  1003.             else if (!istool)
  1004.             c = lower(*buf);
  1005.         }
  1006.         if (c == 'd') {
  1007.             rm_edfile(-2);
  1008.             return 0;
  1009.         } else if (c == 'c') {
  1010.             wprint("(continue editing letter)\n");
  1011.             return -1;
  1012.         } else if (c == 's')
  1013.             break;
  1014.         }
  1015.     }
  1016.     }
  1017.  
  1018.     if (send_it() == -1) {
  1019.     if (isoff(flags, SEND_NOW))
  1020.         wprint("(continue)\n");
  1021.     return -1;
  1022.     }
  1023.     return 0;
  1024. }
  1025.  
  1026. /*
  1027.  * actually send the letter.
  1028.  * 1. reset all the signals because of fork.
  1029.  * 2. determine recipients (users, address, files, programs)
  1030.  * 3. determine mailer, fork and return (if not verbose).
  1031.  * 4. popen mailer, $record, and other files specified in step 1.
  1032.  * 5. make the headers; this includes To: line, and user set headers, etc...
  1033.  * 6. copy the letter right into the array of file pointers (step 1).
  1034.  * 7. close the mailer and other files (step 1) and remove the edit-file.
  1035.  * return -1 if mail wasn't sent.  could be user error, could be the system.
  1036.  * allow user to try again or to save message to file and abort message.
  1037.  * return 0 if successful.
  1038.  */
  1039. static int
  1040. send_it()
  1041. {
  1042.     register char *p, *b, *addr_list;
  1043. #ifdef MAXFILES
  1044.     register int size = MAXFILES - 1;
  1045.     FILE *files[MAXFILES];
  1046.     char *names[MAXFILES];
  1047. #else
  1048.     register int size = getdtablesize() - 1;
  1049.     FILE *files[30];  /* 30 should be sufficiently large enough */
  1050.     char *names[30];
  1051. #endif /* MAXFILES */
  1052. #if defined(VERBOSE_ARG)
  1053.     SIGRET (*oldchld)();
  1054. #endif /* VERBOSE_ARG */
  1055.     int next_file = 1; /* reserve files[0] for the mail delivery program */
  1056.     int log_file = -1; /* the index into the files array for mail logging */
  1057.     char buf[3*HDRSIZ];
  1058.     char *orig_to, *orig_cc, *orig_bcc; /* save originals to restore on error */
  1059.     char expand = !do_set(set_options, "no_expand");
  1060.     int fork_pid;
  1061.  
  1062.     names[0] = names[1] = NULL; /* for free_vec() */
  1063.     /* If edit_hdrs, make sure the correct headers exist and are intact
  1064.      * before bothering to continue.
  1065.      */
  1066.     if (ison(flags, EDIT_HDRS)) {
  1067.     /* fool header_field into thinking that the file is the folder */
  1068.     FILE *save_tmpf = tmpf;
  1069.     long old_offset = msg[msg_cnt].m_offset;
  1070.  
  1071.     if (!ed_fp) {
  1072.         wprint("No file for headers!\n");
  1073.         return -1;
  1074.     }
  1075.     Debug("Getting headers from file ... ");
  1076.  
  1077.     tmpf = ed_fp;
  1078.     msg[msg_cnt].m_offset = 0L;
  1079.     if (p = header_field(msg_cnt, "to")) {
  1080.         (void) strcpy(To, p);
  1081.         Cc[0] = Bcc[0] = 0;
  1082.         if (p = header_field(msg_cnt, "cc"))
  1083.         (void) strcpy(Cc, p);
  1084.         if (p = header_field(msg_cnt, "bcc"))
  1085.         (void) strcpy(Bcc, p);
  1086.         if (p = header_field(msg_cnt, "fcc"))
  1087.         next_file += find_files(p, names+next_file, size-next_file, 1);
  1088.     } else
  1089.         *To = 0; /* Error caught below */
  1090.     msg[msg_cnt].m_offset = old_offset;
  1091.     tmpf = save_tmpf;
  1092.     Debug("\n");
  1093.     }
  1094.     if (!*To) {
  1095.     wprint("You must have a To: recipient to send mail.\n");
  1096.     if (!istool) {
  1097.         (void) signal(SIGINT, oldint);
  1098.         (void) signal(SIGQUIT, oldquit);
  1099.         (void) signal(SIGTERM, oldterm);
  1100.     }
  1101.     free_vec(&names[1]);
  1102.     return -1;
  1103.     }
  1104.  
  1105.     if (!(p = do_set(set_options, "sendmail")))
  1106.     p = MAIL_DELIVERY;
  1107.  
  1108. #ifdef VERBOSE_ARG
  1109.     /* Tool mode can't do verbosity -- no window for the MTA output */
  1110.     if (!istool && (ison(flags, VERBOSE) || do_set(set_options, "verbose"))) {
  1111.     turnon(flags, VERBOSE); /* prevent fork when "verbose" has changed */
  1112.     oldchld = signal(SIGCHLD, SIG_DFL); /* let pclose() do the wait() */
  1113. #if defined(MMDF) && !defined(M_EXECMAIL)
  1114.     b = &buf[strlen(sprintf(buf, "%s%s", p, VERBOSE_ARG))];
  1115. #else /* MMDF */
  1116.     b = &buf[strlen(sprintf(buf, "%s %s", p, VERBOSE_ARG))];
  1117. #endif /* MMDF && !M_EXECMAIL */
  1118.     } else
  1119. #endif /* VERBOSE_ARG */
  1120.     b = buf + Strcpy(buf, p);
  1121. #ifdef METOO_ARG
  1122.     if (!strcmp(p, MAIL_DELIVERY) && do_set(set_options, "metoo"))
  1123.     b += strlen(sprintf(b, " %s", METOO_ARG));
  1124. #endif /* METOO_ARG */
  1125.     *b++ = ' ', *b = 0; /* strcat(b, " "); */
  1126.     addr_list = b; /* save this position to check for addresses later */
  1127.  
  1128.     /* save original list.  If alias expansion fails, replace address lists
  1129.      * with what was originally typed so user can fix it.  This isn't necessary
  1130.      * if the lists are already in the file the user is editing (edit_hdrs).
  1131.      */
  1132.     if (isoff(flags, EDIT_HDRS))
  1133.     orig_to = savestr(To);
  1134.     /*
  1135.      * Build the address lines to give to the mail transfer system.  This
  1136.      * address line cannot contain comment fields!  First, expand aliases
  1137.      * since they may contain comment fields within addresses. Copy this
  1138.      * result back into the Buffer since this will go into the header ...
  1139.      * Next, remove all comments so the buffer contains ONLY valid addresses.
  1140.      * Next, strip off any filenames/programs which might occur in the list.
  1141.      * Finally, add this information to the command line buffer (buf).
  1142.      * Remove commas if necessary (see ifdefs).  In the event of errors,
  1143.      * force a dead letter by rm_edfile(-1).
  1144.      */
  1145.     if (!(p = alias_to_address(To))) {
  1146.     print("address expansion failed for To: list.\n");
  1147.     free_vec(&names[1]);
  1148.     if (isoff(flags, EDIT_HDRS))
  1149.         strcpy(To, orig_to), xfree(orig_to);
  1150.     return -1;
  1151.     } else {
  1152.     next_file += find_files(p, names+next_file, size-next_file, 0);
  1153.     if (expand)
  1154.         (void) strcpy(To, p);
  1155.     rm_cmts_in_addr(p);
  1156.     skipspaces(0);
  1157.     b += Strcpy(b, p);
  1158.     }
  1159.     if (isoff(flags, EDIT_HDRS))
  1160.     orig_cc = savestr(Cc);
  1161.     if (*Cc) {
  1162.     if (!(p = alias_to_address(Cc))) {
  1163.         wprint("address expansion failed for Cc: list.\n");
  1164.         free_vec(&names[1]);
  1165.         if (isoff(flags, EDIT_HDRS)) {
  1166.         strcpy(To, orig_to), xfree(orig_to);
  1167.         strcpy(Cc, orig_cc), xfree(orig_cc);
  1168.         }
  1169.         return -1;
  1170.     } else {
  1171.         next_file += find_files(p, names+next_file, size-next_file, 0);
  1172.         if (expand)
  1173.         (void) strcpy(Cc, p);
  1174.         rm_cmts_in_addr(p);
  1175.         skipspaces(0);
  1176.         if (*p) {
  1177.         *b++ = ',', *b++ = ' ';
  1178.         b += Strcpy(b, p);
  1179.         }
  1180.     }
  1181.     }
  1182.  
  1183.     /* expand Bcc addrs, but don't add to list yet.  sign letter first */
  1184.     if (isoff(flags, EDIT_HDRS))
  1185.     orig_bcc = savestr(Bcc);
  1186.     if (*Bcc) {
  1187.     if (p = alias_to_address(Bcc))
  1188.         p = strcpy(Bcc, p);
  1189.     else {
  1190.         wprint("address expansion failed for Bcc: list.\n");
  1191.         free_vec(&names[1]);
  1192.         /* rm_edfile(-1); */
  1193.         if (isoff(flags, EDIT_HDRS)) {
  1194.         strcpy(To, orig_to), xfree(orig_to);
  1195.         strcpy(Cc, orig_cc), xfree(orig_cc);
  1196.         strcpy(Bcc, orig_bcc), xfree(orig_bcc);
  1197.         }
  1198.         return -1;
  1199.     }
  1200.     } else
  1201.     p = NULL;
  1202.  
  1203.     /* Sign the letter before adding the Bcc list since they aren't
  1204.      * considered when adding a signature.
  1205.      */
  1206.     if (*addr_list && ison(flags, SIGN|DO_FORTUNE) &&
  1207.         isoff(glob_flags, REDIRECT) && isoff(flags, FORWARD))
  1208.     sign_letter(addr_list, flags, ed_fp);
  1209.  
  1210.     if (p) { /* p still points to expanded Bcc list */
  1211.     next_file += find_files(p, names+next_file, size-next_file, 0);
  1212.     rm_cmts_in_addr(p);
  1213.     skipspaces(0);
  1214.     if (*p) {
  1215.         *b++ = ',', *b++ = ' ';
  1216.         b += Strcpy(b, p);
  1217.     }
  1218.     }
  1219.     if (!*addr_list && next_file == 1) {
  1220.     wprint("There must be at least 1 legal recipient.\n");
  1221.     if (isoff(flags, EDIT_HDRS)) {
  1222.         strcpy(To, orig_to), xfree(orig_to);
  1223.         strcpy(Cc, orig_cc), xfree(orig_cc);
  1224.         strcpy(Bcc, orig_bcc), xfree(orig_bcc);
  1225.     }
  1226.     return -1;
  1227.     }
  1228.  
  1229. #ifdef NO_COMMAS
  1230.     for (p = buf; p = index(p, ','); p++)
  1231.     *p = ' ';
  1232. #endif /* NO_COMMAS */
  1233.  
  1234.     Debug("mail command: %s\n", buf);
  1235.  
  1236.     if (isoff(flags, VERBOSE) && debug < 3)
  1237.     switch (fork_pid = fork()) {
  1238.         case  0:  /* the child will send the letter. ignore signals */
  1239. #if defined(SYSV) && !defined(AUX) && !defined(IRIX4)
  1240.         if (setpgrp() == -1)
  1241. #else /* !SYSV || AUX || IRIX4 */
  1242.         if (setpgrp(0, getpid()) == -1)
  1243. #endif /* SYSV && !AUX || IRIX4 */
  1244.             error("setpgrp");
  1245.         /* NOTE: No special case needed for tool here because
  1246.          * this is the sending child -- it's going to pclose()
  1247.          * and then exit(), so who cares about the notifier?
  1248.          */
  1249.         (void) signal(SIGCHLD, SIG_DFL);
  1250.         (void) signal(SIGTERM, SIG_IGN);
  1251.         (void) signal(SIGINT, SIG_IGN);
  1252.         (void) signal(SIGHUP, SIG_IGN);
  1253.         (void) signal(SIGQUIT, SIG_IGN);
  1254. #ifdef SIGTTIN
  1255.         (void) signal(SIGTTOU, SIG_IGN);
  1256.         (void) signal(SIGTTIN, SIG_IGN);
  1257. #endif /* SIGTTIN */
  1258. #ifdef SIGCONT
  1259.         (void) signal(SIGCONT, SIG_IGN);
  1260.         (void) signal(SIGTSTP, SIG_IGN);
  1261. #endif /* SIGCONT */
  1262.         turnon(glob_flags, IGN_SIGS);
  1263.         when -1:
  1264.         error("fork failed trying to send mail");
  1265.         if (isoff(flags, EDIT_HDRS)) {
  1266.             strcpy(To, orig_to);
  1267.             strcpy(Cc, orig_cc);
  1268.             strcpy(Bcc, orig_bcc);
  1269.         }
  1270.         /* fall thru */
  1271.         default:
  1272.         if (fork_pid > 0) {
  1273. #ifdef SUNTOOL
  1274.             /* If we're a tool, we have to register a handler
  1275.              * for the fork_pid.  Otherwise, it's used only as
  1276.              * an error indicator, so reset it to zero.
  1277.              */
  1278.             if (istool)
  1279.             notify_set_wait3_func(mfprint_sw, my_wait3, fork_pid);
  1280. #endif /* SUNTOOL */
  1281.             fork_pid = 0;
  1282.         }
  1283.         /* istool doesn't need ed_fp, so don't keep it around */
  1284.         if (istool || !fork_pid && isoff(glob_flags, REDIRECT))
  1285.             (void) fclose(ed_fp), ed_fp = NULL_FILE;
  1286.         free_vec(&names[1]);
  1287.         if (isoff(flags, EDIT_HDRS))
  1288.             xfree(orig_to), xfree(orig_cc), xfree(orig_bcc);
  1289.         if (!istool) {
  1290.             (void) signal(SIGINT, oldint);
  1291.             (void) signal(SIGQUIT, oldquit);
  1292.             (void) signal(SIGTERM, oldterm);
  1293.         }
  1294.         return fork_pid;
  1295.     }
  1296.  
  1297. #if defined(MMDF) && !defined(M_EXECMAIL)
  1298.     *(addr_list-1) = '\0';
  1299. #endif /* MMDF && !M_EXECMAIL */
  1300.     if (debug > 2) {
  1301.     files[0] = stdout;
  1302.     if (!*addr_list)
  1303.         addr_list = "[no recipients]";
  1304.     } else if (*addr_list) {
  1305.     if (!(files[0] = open_file(buf, TRUE, FALSE))) {
  1306.         rm_edfile(-1); /* force saving of undeliverable mail */
  1307.         if (isoff(flags, VERBOSE) && debug < 3)
  1308.         exit(-1);
  1309.         else
  1310.         return 0;
  1311.     }
  1312.     } else
  1313.     files[0] = NULL_FILE;
  1314.  
  1315.     if (ison(flags, VERBOSE))
  1316.     wprint("Sending letter ... "), (void) fflush(stdout);
  1317. #if defined(MMDF) && !defined(M_EXECMAIL)
  1318.     /* give address list to submit */
  1319.     for (p = addr_list; *p && (p = any(p, ",<")); p++)
  1320.     if (*p == ',')
  1321.         *p = '\n';
  1322.     else
  1323.         p = index(p, '>');
  1324.     if (*addr_list)
  1325.     (void) fprintf(files[0], "%s\n\n", addr_list);
  1326. #endif /* MMDF && !M_EXECMAIL */
  1327.  
  1328.     /* see if log is set.  This is just to add message headers. No msg body. */
  1329.     if (p = do_set(set_options, "logfile")) {
  1330.     if (!*p)
  1331.         p = "~/mail.log";
  1332.     if (!index("~|/+", *p))
  1333.         (void) sprintf(buf, "%s/%s", do_set(set_options, "cwd"), p);
  1334.     else
  1335.         (void) strcpy(buf, p);
  1336.     log_file = next_file;
  1337.     next_file += find_files(buf, names+next_file, size-next_file, 0);
  1338.     if (log_file == next_file)
  1339.         log_file = -1;
  1340.     }
  1341.  
  1342.     /* see if record is set.  If so, open that file for appending and add
  1343.      * the letter in a format such that mail can be read from it
  1344.      */
  1345.     if (p = do_set(set_options, "record")) {
  1346.     if (!*p)
  1347.         p = "~/record";
  1348.     if (!index("~|/+", *p))
  1349.         (void) sprintf(buf, "%s/%s", do_set(set_options, "cwd"), p);
  1350.     else
  1351.         (void) strcpy(buf, p);
  1352.     next_file += find_files(buf, names+next_file, size-next_file, 0);
  1353.     }
  1354.  
  1355.     /* Don't need to open names[0] as files[0], so skip those */
  1356.     next_file = 1 + open_list(names + 1, files + 1, next_file - 1);
  1357.  
  1358.     /* First, put the message separator in... */
  1359.     for (size = 1; size < next_file; size++)
  1360. #ifndef MSG_SEPARATOR
  1361.     {
  1362.         time_t t;
  1363.         (void) time(&t);
  1364.         (void) fprintf(files[size], "From %s %s", login, ctime(&t));
  1365.     }
  1366. #else /* MSG_SEPARATOR */
  1367. #ifdef MMDF
  1368.     (void) fputs(MSG_SEPARATOR, files[size]);
  1369. #else /* MMDF */
  1370.     (void) fprintf(files[size], "%s\n", MSG_SEPARATOR);
  1371. #endif /* MMDF */
  1372. #endif /* MSG_SEPARATOR */
  1373.  
  1374.     /* if redirection, ed_fp = stdin, else rewind the file just made */
  1375.     if (isoff(glob_flags, REDIRECT))
  1376.     rewind(ed_fp);
  1377.     else
  1378.     ed_fp = stdin;
  1379.  
  1380. #ifndef MSG_SEPARATOR
  1381.     /* If forwarding or reading a draft, skip the leading From_ line.
  1382.      * This is done for drafts so that messages saved by dead_letter()
  1383.      * can be read back in as a draft; in other cases, this isn't done
  1384.      * for edit_hdrs because FORWARD wouldn't be set.
  1385.      */
  1386.     if (ison(flags, FORWARD|SEND_NOW) && fgets(buf, sizeof buf, ed_fp) &&
  1387.     strncmp(buf, "From ", 5) != 0)
  1388.     rewind(ed_fp); /* No From_ line (should never happen) */
  1389. #endif /* MSG_SEPARATOR */
  1390.     {
  1391.     long offset = add_headers(ed_fp, files, next_file, flags);
  1392.     if (offset == -1)
  1393.         offset = 0L;
  1394.     (void) fseek(ed_fp, offset, L_SET);
  1395.     }
  1396.  
  1397.     /* Read from stdin or the edfile till EOF and send it all to the mailer
  1398.      * and other open files/folders/programs. Check for "From " at the
  1399.      * beginnings of these lines to prevent creating new messages in folders.
  1400.      */
  1401.     while (fgets(buf, sizeof buf, ed_fp))
  1402.     for (size = 0; size < next_file; size++) {
  1403.         if (!files[size]) /* files[0] will be NULL if not calling MTA */
  1404.         continue;
  1405.         if (size == log_file)
  1406.         continue;
  1407. #ifndef MSG_SEPARATOR
  1408.         if (!strncmp(buf, "From ", 5))
  1409.         (void) fputc('>', files[size]);
  1410. #endif /* MSG_SEPARATOR */
  1411.         if (fputs(buf, files[size]) == EOF) {
  1412.         if (size == 0) {
  1413.             error("Lost connection to MTA");
  1414.             dead_letter(-1);
  1415.             break;
  1416.         } else {
  1417.             /* Drop this file, but continue writing others */
  1418.             if (names[size]) {
  1419.             error("Write failed: %s", names[size]);
  1420.             (void) close_lock(names[size], files[size]);
  1421.             xfree(names[size]);
  1422.             } else
  1423.             error("Write failed");
  1424.             if (size < --next_file) {
  1425.             names[size] = names[next_file];
  1426.             files[size--] = files[next_file];
  1427.             }
  1428.             files[next_file] = NULL_FILE;
  1429.             names[next_file] = NULL;
  1430.         }
  1431.         }
  1432.     }
  1433.  
  1434.     /* loop thru the open files (except for the first: the mail delivery agent)
  1435.      * and append a blank line so that ucb-mail can read these folders.
  1436.      * Then close the files.
  1437.      */
  1438.     for (size = 1; size < next_file; size++) {
  1439. #ifdef END_MSG_SEP
  1440.     (void) fputs(END_MSG_SEP, files[size]);
  1441. #endif /* END_MSG_SEP */
  1442.     if (names[size]) {
  1443. #ifndef END_MSG_SEP
  1444.         (void) fputc('\n', files[size]);
  1445. #endif /* !END_MSG_SEP */
  1446.         if (close_lock(names[size], files[size]) == EOF) {
  1447.         error("Warning: Close failed: %s", names[size]);
  1448.         }
  1449.         xfree(names[size]);
  1450.     } else {
  1451.         if (debug < 3)
  1452.         (void) fclose(files[size]); /* Don't mess with pclose() */
  1453.         else
  1454.         (void) pclose(files[size]); /* unless we never forked */
  1455.     }
  1456.     }
  1457.  
  1458.     if (debug < 3) {
  1459.     int reply_code = files[0]? pclose(files[0]) : (MTA_EXIT << 8);
  1460.     Debug("pclose reply_code = %d\n", reply_code);
  1461.     rm_edfile((reply_code == (MTA_EXIT << 8))? 0 : -1);
  1462.     } else
  1463.     rm_edfile(0);
  1464.  
  1465. #ifdef VERBOSE_ARG
  1466.     if (!istool && ison(flags, VERBOSE))
  1467.     (void) signal(SIGCHLD, oldchld);
  1468. #endif /* VERBOSE_ARG */
  1469.  
  1470.     if (ison(flags, VERBOSE) || debug > 2) {
  1471.     if (isoff(glob_flags, REDIRECT))
  1472.         wprint("sent.\n");
  1473.     if (!istool) {
  1474.         (void) signal(SIGINT, oldint);
  1475.         (void) signal(SIGQUIT, oldquit);
  1476.         (void) signal(SIGTERM, oldterm);
  1477.     }
  1478.     } else
  1479.     exit(0); /* not a user exit -- a child exit */
  1480.     return 0;
  1481. }
  1482.  
  1483. /*
  1484.  * Add the necessary headers to make a file a legitimate mail message.
  1485.  * This could be for a file which the user will edit (via edit_hdrs) or
  1486.  * for delivery to an MTA.
  1487.  * Make folders conform to RFC-822 by adding From: and Date: headers.
  1488.  * Prefix certain header with the "Resent-" prefix when forwarding.
  1489.  * Return offset of fp if we're parsing it for headers (for delivery to MTA).
  1490.  */
  1491. static long
  1492. add_headers(fp, files, size, flags)
  1493. FILE *fp, *files[];
  1494. int size;
  1495. u_long flags;
  1496. {
  1497.     char buf[BUFSIZ], From_buf[256], *pF = From_buf, date_str[64];
  1498.     char *host = NULL, *p, *subj = NULL, *own_from = NULL; /* See WARNING */
  1499.     int i, for_editor = (fp == NULL_FILE);
  1500.     int got_date = for_editor, got_from = for_editor;
  1501.     struct options *opts;
  1502.  
  1503.     if (for_editor && hfile) {
  1504.     i = file_to_fp(hfile, files[0], "r");
  1505.     xfree(hfile), hfile = NULL;
  1506.     return (i < 0 ? -1 : TRUE);
  1507.     }
  1508.  
  1509.     buf[0] = 0;
  1510.     if (ourname)
  1511.     host = ourname[0];
  1512.     if (for_editor)
  1513.     turnoff(flags,FORWARD); /* forwarded messages must not be edited */
  1514.  
  1515.     /* [Re]create a From: header -- check first to see if the user has
  1516.      * created a From: header with the my_hdr command (the own_hdrs list).
  1517.      * If his is not legitimate, warn user and use the other header.
  1518.      */
  1519.     if ((for_editor || isoff(flags, EDIT_HDRS)) &&
  1520.         own_hdrs && !do_set(set_options, "no_hdrs")) {
  1521.     for (opts = own_hdrs; opts; opts = opts->next)
  1522.         if (!strcmp(opts->option, "From:")) {
  1523.         p = opts->value;
  1524.         skipspaces(0);
  1525.         sprintf(buf, "%sFrom: %s\n",
  1526.                 ison(flags, FORWARD)? "Resent-" : "", p);
  1527.         own_from = buf;
  1528.         /* WARNING: the above depends on the following facts:
  1529.          * 1. If for_editor, own_from will be output immediately,
  1530.          *    so buf will not be overwritten;
  1531.          * 2. If !for_editor but EDIT_HDRS, the "real" from line
  1532.          *    will be read from the file so own_from isn't needed;
  1533.          * 3. If neither, From: is the first line output, so
  1534.          *    buf will not be overwritten.
  1535.          * Any change in the above means a new buffer for own_from
  1536.          * may be needed.  Check carefully.
  1537.          */
  1538.         }
  1539.     }
  1540.     if (ison(flags, FORWARD))
  1541.     pF += Strcpy(From_buf, "Resent-");
  1542.     pF += Strcpy(pF, "From: ");
  1543. #ifdef UUCP
  1544.     if (host && *host)
  1545.     pF += strlen(sprintf(pF, "%s!", host));
  1546. #endif /* UUCP */
  1547.     pF += Strcpy(pF, login);
  1548. #ifndef UUCP
  1549.     if (host && *host)
  1550.     pF += strlen(sprintf(pF, "@%s", host));
  1551. #endif /* UUCP */
  1552.     if ((p = do_set(set_options, "realname")) ||
  1553.     (p = do_set(set_options, "name")))
  1554.     pF += strlen(sprintf(pF, " (%s)", p));
  1555.     *pF++ = '\n', *pF++ = 0;
  1556.  
  1557.     /* First print From, Date, In-Reply-To */
  1558.     for (i = 0; i < size; i++) {
  1559.     if (!files[i])
  1560.         continue;
  1561.     if (for_editor)
  1562.         if (own_from)
  1563.         (void) fputs(own_from, files[i]);
  1564.         else
  1565.         (void) fputs(From_buf, files[i]);
  1566.     else if (isoff(flags, EDIT_HDRS)) {
  1567. #ifdef PICKY_MAILER
  1568.         if (i > 0)
  1569. #endif /* PICKY_MAILER */
  1570.         if (own_from)
  1571.         (void) fputs(own_from, files[i]);
  1572.         else
  1573.         (void) fputs(From_buf, files[i]);
  1574.         got_from = TRUE;
  1575.     }
  1576.     if (for_editor || isoff(flags, EDIT_HDRS)) {
  1577. #ifdef PICKY_MAILER
  1578.         if (i > 0 && !for_editor)
  1579. #endif /* PICKY_MAILER */
  1580.         (void) fprintf(files[i], "%sDate: %s\n",
  1581.         ison(flags, FORWARD) ? "Resent-" : "", rfc_date(date_str));
  1582.         got_date = TRUE;
  1583.         if (*in_reply_to)
  1584.         fprintf(files[i], "In-Reply-To: %s\n", in_reply_to);
  1585.     }
  1586.     }
  1587.     if (own_from)
  1588.     *own_from = 0; /* buf[0] must be 0 below */
  1589.     /* next print user's own message headers */
  1590.     if (for_editor || isoff(flags, EDIT_HDRS))
  1591.     if (own_hdrs && !do_set(set_options, "no_hdrs")) {
  1592.         for (opts = own_hdrs; opts; opts = opts->next) {
  1593.         if (!strcmp(opts->option, "From:"))
  1594.             continue;
  1595.         for (i = 0; i < size; i++) {
  1596.             if (!files[i])
  1597.             continue;
  1598.             p = opts->value;
  1599.             skipspaces(0);
  1600.             fprintf(files[i], "%s %s\n", opts->option, p);
  1601.         }
  1602.         }
  1603.     }
  1604.  
  1605.     /*
  1606.      * Now either prepare to put the rest of the headers into the file
  1607.      * or (when sending edited headers) copy them back out of the file
  1608.      */
  1609.     if (for_editor) {
  1610.     char *orig = NULL;
  1611.     /* for edit_hdrs, print the headers followed by a blank line */
  1612.     if (To[0]) {
  1613.         orig = savestr(To);
  1614.         if (!(p = alias_to_address(To))) {
  1615.         wprint("To: list unmodified.\n");
  1616.         p = orig;
  1617.         }
  1618.         (void) strcpy(To, p);
  1619.     }
  1620.     if (Cc[0]) {
  1621.         strdup(orig, Cc);
  1622.         if (!(p = alias_to_address(Cc))) {
  1623.         wprint("Cc: list unmodified.\n");
  1624.         p = orig;
  1625.         }
  1626.         (void) strcpy(Cc, p);
  1627.     }
  1628.     if (Bcc[0]) {
  1629.         strdup(orig, Bcc);
  1630.         if (!(p = alias_to_address(Bcc))) {
  1631.         wprint("Bcc: list unmodified.\n");
  1632.         p = orig;
  1633.         }
  1634.         (void) strcpy(Bcc, p);
  1635.     }
  1636.     xfree(orig);
  1637.     } else if (ison(flags, EDIT_HDRS)) {
  1638.     /* copy the headers of the message removing special headers */
  1639.     int print_hdr = FALSE;
  1640.     if (isoff(flags, SEND_NOW))
  1641.         rewind(fp); /* Drafts may have had fp positioned */
  1642.     while (fgets(buf, sizeof(buf), fp)) {
  1643.         (void) no_newln(buf);
  1644.         if (!buf[0])
  1645.         break;
  1646.         /* if the first char is NOT a space, it MUST be a new header.
  1647.          * Otherwise, it is considered part of the message body.
  1648.          */
  1649.         if (!isspace(buf[0])) {
  1650.         print_hdr = TRUE;
  1651.         if (!(p = any(buf, " \t:")) || isspace(*p))
  1652.             break; /* this is not a legitimate header */
  1653.         skipspaces(1);
  1654.         if (!*p)
  1655.             print_hdr = FALSE; /* blank headers are not allowed */
  1656.         p = buf;
  1657.         if (!lcase_strncmp(buf, "resent-", 7)) {
  1658.             if (ison(flags, EDIT_HDRS))
  1659.         wprint("You can't use \"Resent-\" headers in edited messages.\n");
  1660.             p += 7;
  1661.         }
  1662.         if (!lcase_strncmp(p, "to:", 3) ||
  1663.             !lcase_strncmp(p, "cc:", 3) ||
  1664.             !lcase_strncmp(p, "bcc:", 4) ||
  1665.             !lcase_strncmp(p, "fcc:", 4) ||
  1666.             !lcase_strncmp(p, "x-mailer:", 9) ||
  1667.             !lcase_strncmp(p, "status:", 7))
  1668.             print_hdr = FALSE;
  1669.         else if (!lcase_strncmp(p, "date:", 5))
  1670.             if (got_date)
  1671.             wprint("You can't change or add date headers.\n");
  1672.             else {
  1673.             got_date = TRUE;
  1674.             (void) sprintf(buf, "Date: %s", rfc_date(date_str));
  1675.             p = buf;
  1676.             }
  1677.         else if (!lcase_strncmp(p, "subject:", 8))
  1678.             (print_hdr = FALSE), strdup(subj, p);
  1679.         else if (!lcase_strncmp(p, "from:", 5)) {
  1680.             char not_me[BUFSIZ];
  1681.             (void) strcpy(not_me, buf + 5);
  1682.             take_me_off(not_me);
  1683.             if (*not_me) {
  1684.             /* Ignore bogus From: if we have a good one */
  1685.             if (got_from)
  1686.                 print_hdr = FALSE;
  1687.             /* otherwise, output a good one */
  1688.             else {
  1689.                 (void) strcpy(buf, From_buf);
  1690.                 (void) no_newln(buf);
  1691.             }
  1692.             }
  1693.             got_from = TRUE;
  1694. #ifdef PICKY_MAILER
  1695.             /* don't send From: to mta -- fool "for loop" below
  1696.              * by initializing the loop at files[1], not files[0]
  1697.              */
  1698.             if (!for_editor)
  1699.             print_hdr = 2;
  1700. #endif /* PICKY_MAILER */
  1701.         }
  1702.         }
  1703.         if (print_hdr)
  1704.         /* print_hdr may be 2 for From: header */
  1705.         for (i = print_hdr-1; i < size; i++)
  1706.             if (files[i]) {
  1707.             (void) fputs(buf, files[i]);
  1708.             (void) fputc('\n', files[i]);
  1709.             }
  1710.     }
  1711.     }
  1712.     /* Finally, do the required (or changed) headers (Date, To, Cc) */
  1713.     (void) wrap_addrs(To, 80);
  1714.     (void) wrap_addrs(Cc, 80);
  1715.     (void) wrap_addrs(Bcc, 80);
  1716.     for (i = 0; i < size; i++) {
  1717.     if (!files[i])
  1718.         continue;
  1719. #ifdef PICKY_MAILER
  1720.     if (i > 0) {
  1721. #endif /* PICKY_MAILER */
  1722.     if (!got_from)
  1723.         (void) fputs(From_buf, files[i]);
  1724.     if (!got_date)
  1725.         (void) fprintf(files[i], "%sDate: %s\n",
  1726.         ison(flags, FORWARD) ? "Resent-" : "", rfc_date(date_str));
  1727. #ifdef PICKY_MAILER
  1728.     }
  1729. #endif /* PICKY_MAILER */
  1730.     (void) fprintf(files[i], "X-Mailer: %s\n", check_internal("version"));
  1731.     (void) fprintf(files[i], "%sTo: %s\n",
  1732.         ison(flags, FORWARD) ? "Resent-" : "", To);
  1733.     if (for_editor || isoff(flags, EDIT_HDRS)) {
  1734.         if (isoff(flags, FORWARD) &&
  1735.             (*Subject || for_editor && (do_set(set_options, "ask") ||
  1736.                         do_set(set_options, "asksub"))))
  1737.         (void) fprintf(files[i], "Subject: %s\n", Subject);
  1738.     } else if (subj && *subj && strlen(subj) > 9)
  1739.         (void) (fputs(subj, files[i]), fputc('\n', files[i]));
  1740.     if (*Cc || for_editor && do_set(set_options, "askcc"))
  1741.         (void) fprintf(files[i], "%sCc: %s\n",
  1742.         ison(flags, FORWARD) ? "Resent-" : "", Cc);
  1743.     if (i > 0 || for_editor)
  1744.         /* Do not send these to mail transfer agent */
  1745.         if (*Bcc)
  1746.         (void) fprintf(files[i], "%sBcc: %s\n",
  1747.             ison(flags, FORWARD) ? "Resent-" : "", Bcc);
  1748.     if (i > 0)
  1749.         (void) fprintf(files[i], "Status: OR\n");
  1750.     }
  1751.     for (i = 0; i < size; i++)
  1752.     if (files[i])
  1753.         (void) fflush(files[i]);
  1754.     if (buf[0]) /* last attempted header read was a line of msg text */
  1755.     for (i = 0; i < size; i++) {
  1756.         if (files[i]) {
  1757.         (void) fputs(buf, files[i]);
  1758.         (void) fputc('\n', files[i]);
  1759.         (void) fflush(files[i]);
  1760.         }
  1761.     }
  1762.     else
  1763.     if (isoff(flags, FORWARD))
  1764.         for (i = 0; i < size; i++)
  1765.         if (files[i]) {
  1766.             (void) fputc('\n', files[i]);
  1767.             (void) fflush(files[i]);
  1768.         }
  1769.     return fp? ftell(fp) : (long)TRUE;
  1770. }
  1771.  
  1772. /* ARGSUSED */
  1773. SIGRET
  1774. rm_edfile(sig)
  1775. {
  1776.     if (sig > 0) {
  1777.     char *fix;
  1778.     if (ison(glob_flags, IGN_SIGS))
  1779.         return;
  1780.     /* wrapcolumn may have been trashed -- restore it */
  1781.     if ((fix = do_set(set_options, "wrapcolumn")) && *fix)
  1782.         wrapcolumn = atoi(fix);
  1783.     mac_flush(); /* abort pending macros */
  1784.     }
  1785.     /* now check whether we should abort the letter */
  1786.     if (sig > 0 && !killme && ison(glob_flags, IS_GETTING)) {
  1787.     if (!istool)
  1788.         (void) signal(sig, rm_edfile);
  1789.     killme = 1;
  1790.     print("\n** interrupt -- one more to kill letter **\n");
  1791.     longjmp(cntrl_c_buf, 1);
  1792.     }
  1793.     killme = 0;
  1794.     /* if sig == -1, force a save into dead.letter.
  1795.      * else, check for nosave not being set and save anyway if it's not set
  1796.      * sig == 0 indicates normal exit (or ~x), so don't save a dead letter.
  1797.      */
  1798.     if (sig == -1 || sig != 0 && !do_set(set_options, "nosave"))
  1799.     dead_letter(sig);
  1800.     if (isoff(glob_flags, REDIRECT) && ed_fp) /* ed_fp may be null in toolmode*/
  1801.     (void) fclose(ed_fp), ed_fp = NULL_FILE;
  1802.     (void) unlink(edfile);
  1803.  
  1804.     turnoff(glob_flags, IS_GETTING);
  1805.     if (sig == -1)
  1806.     return;
  1807.  
  1808.     if (sig == SIGHUP)
  1809.     cleanup(0);
  1810.     if (!istool) {
  1811.     (void) signal(SIGINT, oldint);
  1812.     (void) signal(SIGQUIT, oldquit);
  1813.     (void) signal(SIGTERM, oldterm);
  1814.     }
  1815.  
  1816.     if (sig == 0 || sig == -2 || istool) /* make sure sigchld is reset first */
  1817.     return;
  1818.  
  1819.     if (isoff(glob_flags, DO_SHELL)) {  /* If we're not in a shell, exit */
  1820.     puts("exiting");
  1821.     echo_on();
  1822.     exit(1);
  1823.     }
  1824.     longjmp(jmpbuf, 1);
  1825. }
  1826.  
  1827. /* save letter into dead letter */
  1828. dead_letter(sig)
  1829. int sig;    /* signal passed to rm_edfile() or 0 */
  1830. {
  1831.     char     *p, buf[BUFSIZ];
  1832.     long     t;
  1833.     FILE     *dead;
  1834.  
  1835.     if (ison(glob_flags, REDIRECT)) {
  1836.     print("input redirected -- can't save dead letter.\n");
  1837.     return;
  1838.     }
  1839.     /* If the file doesn't exist, get outta here. File may not exist if
  1840.      * user generated a ^C from a promptable header and catch sent us here.
  1841.      */
  1842.     if (!ed_fp && Access(edfile, R_OK) != 0)
  1843.     return;
  1844.     /* User may have killed mush via a signal while he was in an editor.
  1845.      * ed_fp will be NULL in this case.  Since the file does exist (above),
  1846.      * open it so we can copy it to dead letter.
  1847.      */
  1848.     if (!ed_fp && !(ed_fp = fopen(edfile, "r"))) {
  1849.     error("can't save dead letter from %s", edfile);
  1850.     return;
  1851.     }
  1852.     /* don't save a dead letter if there's nothing to save. */
  1853.     if (fseek(ed_fp, 0L, 2) || ftell(ed_fp) <= 1L)
  1854.     return;
  1855.     if (!(p = do_set(set_options, "dead")))
  1856.     p = "~/dead.letter";
  1857.     if (!(dead = open_file(p + (*p == '|'), (*p == '|'), TRUE)))
  1858.     return;
  1859.     (void) time (&t);
  1860.     (void) fflush(ed_fp);
  1861.     rewind(ed_fp);
  1862. #ifdef MSG_SEPARATOR
  1863.     (void) fputs(MSG_SEPARATOR, dead);
  1864. #ifndef MMDF
  1865.     (void) fputc('\n', dead);
  1866. #endif /* MMDF */
  1867. #else /* MSG_SEPARATOR */
  1868.     (void) fprintf(dead, "From %s %s", login, ctime(&t));
  1869. #endif /* MSG_SEPARATOR */
  1870.     (void) fprintf(dead, "From: %s\nTo: %s\nSubject: %s\n", login, To, Subject);
  1871.     (void) fprintf(dead, "Date: %s\n", rfc_date(buf));
  1872.     if (*Cc)
  1873.     (void) fprintf(dead, "Cc: %s\n", Cc);
  1874.     if (*Bcc)
  1875.     (void) fprintf(dead, "Bcc: %s\n", Bcc);
  1876.     (void) fputc('\n', dead);
  1877.     while (fgets(buf, sizeof(buf), ed_fp))
  1878.     (void) fputs(buf, dead);
  1879.     (void) fputc('\n', dead);
  1880. #ifdef END_MSG_SEP
  1881.     (void) fputs(END_MSG_SEP, dead);
  1882. #endif /* END_MSG_SEP */
  1883.     if (*p != '|')
  1884.     (void) close_lock(p, dead);
  1885.     else
  1886.     (void) pclose(dead);
  1887.     wprint("Saved%s letter in %s.\n", sig > 0? " unfinished" : "", p);
  1888. }
  1889.