/* * User aborted during message composition. * Save the partial message in ~/dead.letter. */ int collabort(void) { /* * the control flow is subtle, because we can be called from ~q. */ if (hadintr == 0 && isatty(0)) { if (value("ignore") != NULL) { puts("@"); fflush(stdout); clearerr(stdin); } else { fflush(stdout); fputs("\n(Interrupt -- one more to kill letter)\n", stderr); hadintr++; } return(0); } fflush(stdout); rewind(collf); if (value("nosave") == NULL) savedeadletter(collf); return(1); }
/*ARGSUSED*/ void collhup(int s __unused) { rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); }
/*ARGSUSED*/ void collint(int s __unused) { /* * the control flow is subtle, because we can be called from ~q. */ if (!hadintr) { if (value("ignore") != NULL) { printf("@"); (void)fflush(stdout); clearerr(stdin); return; } hadintr = 1; longjmp(colljmp, 1); } rewind(collf); if (value("nosave") == NULL) savedeadletter(collf); longjmp(collabort, 1); }
/*ARGSUSED*/ static void collint(int s) { /* * the control flow is subtle, because we can be called from ~q. */ if (!hadintr) { if (value("ignore") != NULL) { puts("@"); fflush(stdout); clearerr(stdin); return; } hadintr = 1; siglongjmp(colljmp, 1); } exit_status |= 04; rewind(collf); if (value("save") != NULL && s != 0) savedeadletter(collf); siglongjmp(collabort, 1); }
FILE * collect(struct header *hp, int printheaders) { FILE *fbuf; int lc, cc, fd, c, t, lastlong, rc, sig; int escape, eofcount, longline; char getsub; char linebuf[LINESIZE], tempname[PATHSIZE], *cp; collf = NULL; eofcount = 0; hadintr = 0; lastlong = 0; longline = 0; if ((cp = value("escape")) != NULL) escape = *cp; else escape = ESCAPE; noreset++; (void)snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); if ((fd = mkstemp(tempname)) == -1 || (collf = Fdopen(fd, "w+")) == NULL) { warn("%s", tempname); goto err; } (void)rm(tempname); /* * If we are going to prompt for a subject, * refrain from printing a newline after * the headers (since some people mind). */ t = GTO|GSUBJECT|GCC|GNL; getsub = 0; if (hp->h_subject == NULL && value("interactive") != NULL && (value("ask") != NULL || value("asksub") != NULL)) t &= ~GNL, getsub++; if (printheaders) { puthead(hp, stdout, t); fflush(stdout); } if (getsub && gethfromtty(hp, GSUBJECT) == -1) goto err; if (0) { cont: /* Come here for printing the after-suspend message. */ if (isatty(0)) { puts("(continue)"); fflush(stdout); } } for (;;) { c = readline(stdin, linebuf, LINESIZE, &sig); /* Act on any signal caught during readline() ignoring 'c' */ switch (sig) { case 0: break; case SIGINT: if (collabort()) goto err; continue; case SIGHUP: rewind(collf); savedeadletter(collf); /* * Let's pretend nobody else wants to clean up, * a true statement at this time. */ exit(1); default: /* Stopped due to job control */ (void)kill(0, sig); goto cont; } /* No signal, check for error */ if (c < 0) { if (value("interactive") != NULL && value("ignoreeof") != NULL && ++eofcount < 25) { puts("Use \".\" to terminate letter"); continue; } break; } lastlong = longline; longline = (c == LINESIZE - 1); eofcount = 0; hadintr = 0; if (linebuf[0] == '.' && linebuf[1] == '\0' && value("interactive") != NULL && !lastlong && (value("dot") != NULL || value("ignoreeof") != NULL)) break; if (linebuf[0] != escape || value("interactive") == NULL || lastlong) { if (putline(collf, linebuf, !longline) < 0) goto err; continue; } c = linebuf[1]; switch (c) { default: /* * On double escape, just send the single one. * Otherwise, it's an error. */ if (c == escape) { if (putline(collf, &linebuf[1], !longline) < 0) goto err; else break; } puts("Unknown tilde escape."); break; case '!': /* * Shell escape, send the balance of the * line to sh -c. */ shell(&linebuf[2]); break; case ':': case '_': /* * Escape to command mode, but be nice! */ execute(&linebuf[2], 1); goto cont; case '.': /* * Simulate end of file on input. */ goto out; case 'q': /* * Force a quit of sending mail. * Act like an interrupt happened. */ hadintr++; collabort(); fputs("Interrupt\n", stderr); goto err; case 'x': /* * Force a quit of sending mail. * Do not save the message. */ goto err; case 'h': /* * Grab a bunch of headers. */ grabh(hp, GTO|GSUBJECT|GCC|GBCC); goto cont; case 't': /* * Add to the To list. */ hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); break; case 's': /* * Set the Subject list. */ cp = &linebuf[2]; while (isspace(*cp)) cp++; hp->h_subject = savestr(cp); break; case 'c': /* * Add to the CC list. */ hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); break; case 'b': /* * Add stuff to blind carbon copies list. */ hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); break; case 'd': linebuf[2] = '\0'; strlcat(linebuf, getdeadletter(), sizeof(linebuf)); /* fall into . . . */ case 'r': case '<': /* * Invoke a file: * Search for the file name, * then open it and copy the contents to collf. */ cp = &linebuf[2]; while (isspace(*cp)) cp++; if (*cp == '\0') { puts("Interpolate what file?"); break; } cp = expand(cp); if (cp == NULL) break; if (isdir(cp)) { printf("%s: Directory\n", cp); break; } if ((fbuf = Fopen(cp, "r")) == NULL) { warn("%s", cp); break; } printf("\"%s\" ", cp); fflush(stdout); lc = 0; cc = 0; while ((rc = readline(fbuf, linebuf, LINESIZE, NULL)) >= 0) { if (rc != LINESIZE - 1) lc++; if ((t = putline(collf, linebuf, rc != LINESIZE-1)) < 0) { (void)Fclose(fbuf); goto err; } cc += t; } (void)Fclose(fbuf); printf("%d/%d\n", lc, cc); break; case 'w': /* * Write the message on a file. */ cp = &linebuf[2]; while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0') { fputs("Write what file!?\n", stderr); break; } if ((cp = expand(cp)) == NULL) break; rewind(collf); exwrite(cp, collf, 1); break; case 'm': case 'M': case 'f': case 'F': /* * Interpolate the named messages, if we * are in receiving mail mode. Does the * standard list processing garbage. * If ~f is given, we don't shift over. */ if (forward(linebuf + 2, collf, tempname, c) < 0) goto err; goto cont; case '?': if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { warn(_PATH_TILDE); break; } while ((t = getc(fbuf)) != EOF) (void)putchar(t); (void)Fclose(fbuf); break; case 'p': /* * Print out the current state of the * message without altering anything. */ rewind(collf); puts("-------\nMessage contains:"); puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); while ((t = getc(collf)) != EOF) (void)putchar(t); goto cont; case '|': /* * Pipe message through command. * Collect output as new message. */ rewind(collf); mespipe(collf, &linebuf[2]); goto cont; case 'v': case 'e': /* * Edit the current message. * 'e' means to use EDITOR * 'v' means to use VISUAL */ rewind(collf); mesedit(collf, c); goto cont; } } if (value("interactive") != NULL) { if (value("askcc") != NULL || value("askbcc") != NULL) { if (value("askcc") != NULL) { if (gethfromtty(hp, GCC) == -1) goto err; } if (value("askbcc") != NULL) { if (gethfromtty(hp, GBCC) == -1) goto err; } } else { puts("EOT"); (void)fflush(stdout); } } goto out; err: if (collf != NULL) { (void)Fclose(collf); collf = NULL; } out: if (collf != NULL) rewind(collf); noreset--; return(collf); }
/* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(struct header *hp, int printheaders) { char *cp; char *nbuf; int pid; char **namelist; struct name *to, *nsto; FILE *mtf; /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders)) == NULL) return; if (value("interactive") != NULL) { if (value("askcc") != NULL || value("askbcc") != NULL) { if (value("askcc") != NULL) grabh(hp, GCC); if (value("askbcc") != NULL) grabh(hp, GBCC); } else { printf("EOT\n"); (void)fflush(stdout); } } if (fsize(mtf) == 0) { if (value("dontsendempty") != NULL) goto out; if (hp->h_subject == NULL) printf("No message, no subject; hope that's ok\n"); else printf("Null message body; hope that's ok\n"); } /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); if (to == NULL) { printf("No recipients specified\n"); senderr++; } /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ to = outof(to, mtf, hp); if (senderr) savedeadletter(mtf); to = elide(to); if (count(to) == 0) goto out; if (value("recordrecip") != NULL) { /* * Before fixing the header, save old To:. * We do this because elide above has sorted To: list, and * we would like to save message in a file named by the first * recipient the user has entered, not the one being the first * after sorting happened. */ if ((nsto = malloc(sizeof(struct name))) == NULL) err(1, "Out of memory"); bcopy(hp->h_to, nsto, sizeof(struct name)); } fixhead(hp, to); if ((mtf = infix(hp, mtf)) == NULL) { fprintf(stderr, ". . . message lost, sorry.\n"); return; } namelist = unpack(cat(hp->h_smopts, to)); if (debug) { char **t; printf("Sendmail arguments:"); for (t = namelist; *t != NULL; t++) printf(" \"%s\"", *t); printf("\n"); goto out; } if (value("recordrecip") != NULL) { /* * Extract first recipient username from saved To: and use it * as a filename. */ if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL) err(1, "Out of memory"); if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL) (void)savemail(expand(nbuf), mtf); free(nbuf); free(nsto); } else if ((cp = value("record")) != NULL) (void)savemail(expand(cp), mtf); /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated * far above. */ pid = fork(); if (pid == -1) { warn("fork"); savedeadletter(mtf); goto out; } if (pid == 0) { sigset_t nset; (void)sigemptyset(&nset); (void)sigaddset(&nset, SIGHUP); (void)sigaddset(&nset, SIGINT); (void)sigaddset(&nset, SIGQUIT); (void)sigaddset(&nset, SIGTSTP); (void)sigaddset(&nset, SIGTTIN); (void)sigaddset(&nset, SIGTTOU); prepare_child(&nset, fileno(mtf), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = _PATH_SENDMAIL; execv(cp, namelist); warn("%s", cp); _exit(1); } if (value("verbose") != NULL) (void)wait_child(pid); else free_child(pid); out: (void)Fclose(mtf); }
/* * Mail a message on standard input to the people indicated * in the passed header. (Internal interface). */ void mail1(struct header *hp, int printheaders) { char *cp; pid_t pid; char **namelist; struct name *to; FILE *mtf; /* * Collect user's mail from standard input. * Get the result as mtf. */ if ((mtf = collect(hp, printheaders)) == NULL) return; if (fsize(mtf) == 0) { if (value("skipempty") != NULL) goto out; if (hp->h_subject == NULL || *hp->h_subject == '\0') puts("No message, no subject; hope that's ok"); else puts("Null message body; hope that's ok"); } /* * Now, take the user names from the combined * to and cc lists and do all the alias * processing. */ senderr = 0; to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); if (to == NULL) { puts("No recipients specified"); senderr++; } /* * Look through the recipient list for names with /'s * in them which we write to as files directly. */ to = outof(to, mtf, hp); if (senderr) savedeadletter(mtf); to = elide(to); if (count(to) == 0) goto out; fixhead(hp, to); if ((mtf = infix(hp, mtf)) == NULL) { fputs(". . . message lost, sorry.\n", stderr); return; } namelist = unpack(hp->h_smopts, to); if (debug) { char **t; fputs("Sendmail arguments:", stdout); for (t = namelist; *t != NULL; t++) printf(" \"%s\"", *t); putchar('\n'); goto out; } if ((cp = value("record")) != NULL) (void)savemail(expand(cp), mtf); /* * Fork, set up the temporary mail file as standard * input for "mail", and exec with the user list we generated * far above. */ pid = fork(); if (pid == -1) { warn("fork"); savedeadletter(mtf); goto out; } if (pid == 0) { sigset_t nset; sigemptyset(&nset); sigaddset(&nset, SIGHUP); sigaddset(&nset, SIGINT); sigaddset(&nset, SIGQUIT); sigaddset(&nset, SIGTSTP); sigaddset(&nset, SIGTTIN); sigaddset(&nset, SIGTTOU); prepare_child(&nset, fileno(mtf), -1); if ((cp = value("sendmail")) != NULL) cp = expand(cp); else cp = _PATH_SENDMAIL; execv(cp, namelist); warn("%s", cp); _exit(1); } if (value("verbose") != NULL) (void)wait_child(pid); else free_child(pid); out: (void)Fclose(mtf); }