#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
#else
__RCSID("$NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $");
#endif
#endif
#include <assert.h>
#include <util.h>
#include "rcv.h"
#include "extern.h"
#include "format.h"
#ifdef MIME_SUPPORT
#include "mime.h"
#endif
#include "sig.h"
#include "thread.h"
static FILE *collf;
static int hadintr;
static jmp_buf abort_jmpbuf;
static jmp_buf reset_jmpbuf;
static int reset_on_stop;
static int
exwrite(const char name[], FILE *fp, int f)
{
FILE *of;
int c;
long cc;
int lc;
struct stat junk;
if (f) {
(void)printf("\"%s\" ", name);
(void)fflush(stdout);
}
if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
if (!f)
(void)fprintf(stderr, "%s: ", name);
(void)fprintf(stderr, "File exists\n");
return -1;
}
if ((of = Fopen(name, "we")) == NULL) {
warn("%s", name);
return -1;
}
lc = 0;
cc = 0;
while ((c = getc(fp)) != EOF) {
cc++;
if (c == '\n')
lc++;
(void)putc(c, of);
if (ferror(of)) {
warn("%s", name);
(void)Fclose(of);
return -1;
}
}
(void)Fclose(of);
(void)printf("%d/%ld\n", lc, cc);
(void)fflush(stdout);
return 0;
}
static void
mesedit(FILE *fp, int c)
{
struct sigaction osa;
sigset_t oset;
FILE *nf;
sig_check();
(void)sig_ignore(SIGINT, &osa, &oset);
nf = run_editor(fp, (off_t)-1, c, 0);
if (nf != NULL) {
(void)fseek(nf, 0L, 2);
collf = nf;
(void)Fclose(fp);
}
(void)sig_restore(SIGINT, &osa, &oset);
sig_check();
}
static void
mespipe(FILE *fp, char cmd[])
{
FILE *nf;
struct sigaction osa;
sigset_t oset;
const char *shellcmd;
int fd;
char tempname[PATHSIZE];
sig_check();
(void)sig_ignore(SIGINT, &osa, &oset);
(void)snprintf(tempname, sizeof(tempname),
"%s/mail.ReXXXXXXXXXX", tmpdir);
if ((fd = mkstemp(tempname)) == -1 ||
(nf = Fdopen(fd, "wef+")) == NULL) {
if (fd != -1)
(void)close(fd);
warn("%s", tempname);
goto out;
}
(void)unlink(tempname);
if ((shellcmd = value(ENAME_SHELL)) == NULL)
shellcmd = _PATH_CSHELL;
if (run_command(shellcmd,
NULL, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
(void)Fclose(nf);
goto out;
}
if (fsize(nf) == 0) {
(void)fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
(void)Fclose(nf);
goto out;
}
(void)fseek(nf, 0L, 2);
collf = nf;
(void)Fclose(fp);
out:
(void)sig_restore(SIGINT, &osa, &oset);
sig_check();
}
static int
interpolate(char ms[], FILE *fp, char *fn, int f)
{
int *msgvec;
struct ignoretab *ig;
const char *tabst;
#ifdef MIME_SUPPORT
struct mime_info *mip;
int retval;
#endif
msgvec = salloc((get_msgCount() + 1) * sizeof(*msgvec));
if (msgvec == NULL)
return 0;
if (getmsglist(ms, msgvec, 0) < 0)
return 0;
if (*msgvec == 0) {
*msgvec = first(0, MMNORM);
if (*msgvec == 0) {
(void)printf("No appropriate messages\n");
return 0;
}
msgvec[1] = 0;
}
if (f == 'f' || f == 'F')
tabst = NULL;
else if ((tabst = value(ENAME_INDENTPREFIX)) == NULL)
tabst = "\t";
ig = isupper(f) ? NULL : ignore;
(void)printf("Interpolating:");
for (; *msgvec != 0; msgvec++) {
struct message *mp;
char *fmtstr;
mp = get_message(*msgvec);
touch(mp);
(void)printf(" %d", *msgvec);
(void)fflush(stdout);
if (tabst && (fmtstr = value(ENAME_INDENT_PREAMBLE)) != NULL)
fmsgprintf(collf, fmtstr, mp);
#ifdef MIME_SUPPORT
mip = NULL;
if (value(ENAME_MIME_DECODE_MSG)) {
if ((tabst == NULL && value(ENAME_MIME_DECODE_INSERT)) ||
(tabst != NULL && value(ENAME_MIME_DECODE_QUOTE)))
mip = mime_decode_open(mp);
}
retval = mime_sendmessage(mp, fp, ig, tabst, mip);
mime_decode_close(mip);
if (retval < 0)
#else
if (sendmessage(mp, fp, ig, tabst, NULL) < 0)
#endif
{
warn("%s", fn);
return -1;
}
if (tabst && (fmtstr = value(ENAME_INDENT_POSTSCRIPT)) != NULL)
fmsgprintf(collf, fmtstr, mp);
}
(void)printf("\n");
return 0;
}
PUBLIC void
savedeadletter(FILE *fp)
{
FILE *dbuf;
mode_t m;
int c;
const char *cp;
if (fsize(fp) == 0)
return;
cp = getdeadletter();
m = umask(077);
dbuf = Fopen(cp, "ae");
(void)umask(m);
if (dbuf == NULL)
return;
(void)printf("Saving message body to `%s'.\n", cp);
while ((c = getc(fp)) != EOF)
(void)putc(c, dbuf);
(void)Fclose(dbuf);
rewind(fp);
}
static void
coll_int(int signo)
{
sig_t o = signal(SIGINT, SIG_IGN);
if (!hadintr) {
if (value(ENAME_IGNORE) != NULL) {
(void)puts("@");
(void)fflush(stdout);
clearerr(stdin);
signal(SIGINT, o);
return;
}
hadintr = 1;
signal(SIGINT, o);
longjmp(reset_jmpbuf, signo);
}
if (collf) {
rewind(collf);
if (value(ENAME_NOSAVE) == NULL)
savedeadletter(collf);
}
signal(SIGINT, o);
longjmp(abort_jmpbuf, signo);
}
__dead static void
coll_hup(int signo __unused)
{
rewind(collf);
savedeadletter(collf);
exit(EXIT_FAILURE);
}
static void
coll_stop(int signo)
{
if (reset_on_stop) {
reset_on_stop = 0;
hadintr = 0;
longjmp(reset_jmpbuf, signo);
}
}
PUBLIC FILE *
collect(struct header *hp, int printheaders)
{
sig_t volatile old_sigint = sig_current(SIGINT);
sig_t volatile old_sighup = sig_current(SIGHUP);
sig_t volatile old_sigtstp = sig_current(SIGTSTP);
sig_t volatile old_sigttin = sig_current(SIGTTIN);
sig_t volatile old_sigttou = sig_current(SIGTTOU);
FILE *fbuf;
int lc, cc;
int c, fd, t;
char linebuf[LINESIZE];
const char *cp;
char tempname[PATHSIZE];
char mailtempname[PATHSIZE];
int eofcount;
int longline;
int lastlong, rc;
char volatile getsub;
int volatile escape;
(void)memset(mailtempname, 0, sizeof(mailtempname));
collf = NULL;
if (setjmp(abort_jmpbuf) || setjmp(reset_jmpbuf)) {
(void)rm(mailtempname);
goto err;
}
sig_check();
sig_hold();
old_sigint = sig_signal(SIGINT, coll_int);
old_sighup = sig_signal(SIGHUP, coll_hup);
old_sigtstp = sig_signal(SIGTSTP, coll_stop);
old_sigttin = sig_signal(SIGTTIN, coll_stop);
old_sigttou = sig_signal(SIGTTOU, coll_stop);
sig_release();
noreset++;
(void)snprintf(mailtempname, sizeof(mailtempname),
"%s/mail.RsXXXXXXXXXX", tmpdir);
if ((fd = mkstemp(mailtempname)) == -1 ||
(collf = Fdopen(fd, "wef+")) == NULL) {
if (fd != -1)
(void)close(fd);
warn("%s", mailtempname);
goto err;
}
(void)rm(mailtempname);
t = GTO | GSUBJECT | GCC | GNL | GSMOPTS;
getsub = 0;
if (hp->h_subject == NULL && value(ENAME_INTERACTIVE) != NULL &&
(value(ENAME_ASK) != NULL || value(ENAME_ASKSUB) != NULL)) {
t &= ~GNL;
getsub++;
}
if (printheaders) {
(void)puthead(hp, stdout, t);
(void)fflush(stdout);
}
if ((cp = value(ENAME_ESCAPE)) != NULL)
escape = *cp;
else
escape = ESCAPE;
hadintr = 0;
if (setjmp(reset_jmpbuf) == 0) {
if (getsub)
(void)grabh(hp, GSUBJECT);
} else {
cont:
if (hadintr) {
(void)fflush(stdout);
(void)fprintf(stderr,
"\n(Interrupt -- one more to kill letter)\n");
} else {
(void)printf("(continue)\n");
(void)fflush(stdout);
}
}
eofcount = 0;
longline = 0;
for (;;) {
reset_on_stop = 1;
c = readline(stdin, linebuf, LINESIZE, reset_on_stop);
reset_on_stop = 0;
if (c < 0) {
char *p;
if (value(ENAME_INTERACTIVE) != NULL &&
(p = value(ENAME_IGNOREEOF)) != NULL &&
++eofcount < (*p == 0 ? 25 : atoi(p))) {
(void)printf("Use \".\" to terminate letter\n");
continue;
}
break;
}
lastlong = longline;
longline = c == LINESIZE - 1;
eofcount = 0;
hadintr = 0;
if (linebuf[0] == '.' && linebuf[1] == '\0' &&
value(ENAME_INTERACTIVE) != NULL && !lastlong &&
(value(ENAME_DOT) != NULL || value(ENAME_IGNOREEOF) != NULL))
break;
if (linebuf[0] != escape || value(ENAME_INTERACTIVE) == NULL ||
lastlong) {
if (putline(collf, linebuf, !longline) < 0)
goto err;
continue;
}
c = linebuf[1];
switch (c) {
default:
if (c == escape) {
if (putline(collf, &linebuf[1], !longline) < 0)
goto err;
else
break;
}
(void)printf("Unknown tilde escape.\n");
break;
#ifdef MIME_SUPPORT
case '@':
hp->h_attach = mime_attach_files(hp->h_attach, &linebuf[2]);
break;
#endif
case 'C':
(void)core(NULL);
break;
case '!':
(void)shell(&linebuf[2]);
break;
case ':':
case '_':
(void)execute(&linebuf[2], ec_composing);
goto cont;
case '.':
goto out;
case 'q':
hadintr++;
coll_int(SIGINT);
exit(1);
case 'x':
goto err;
case 'h':
(void)grabh(hp, GTO | GSUBJECT | GCC | GBCC | GSMOPTS);
goto cont;
case 't':
hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
break;
case 's':
cp = skip_WSP(&linebuf[2]);
hp->h_subject = savestr(cp);
break;
case 'c':
hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
break;
case 'b':
hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
break;
case 'i':
case 'A':
case 'a':
switch(c) {
case 'i':
cp = skip_WSP(&linebuf[2]);
break;
case 'a':
cp = "sign";
break;
case 'A':
cp = "Sign";
break;
default:
goto err;
}
if (*cp && (cp = value(cp)) != NULL) {
(void)printf("%s\n", cp);
if (putline(collf, cp, 1) < 0)
goto err;
}
break;
case 'd':
(void)strcpy(linebuf + 2, getdeadletter());
case 'r':
case '<':
cp = skip_WSP(&linebuf[2]);
if (*cp == '\0') {
(void)printf("Interpolate what file?\n");
break;
}
cp = expand(cp);
if (cp == NULL)
break;
if (*cp == '!') {
const char *shellcmd;
int nullfd;
int rc2;
if ((nullfd = open("/dev/null", O_RDONLY, 0)) == -1) {
warn("/dev/null");
break;
}
(void)snprintf(tempname, sizeof(tempname),
"%s/mail.ReXXXXXXXXXX", tmpdir);
if ((fd = mkstemp(tempname)) == -1 ||
(fbuf = Fdopen(fd, "wef+")) == NULL) {
if (fd != -1)
(void)close(fd);
warn("%s", tempname);
break;
}
(void)unlink(tempname);
if ((shellcmd = value(ENAME_SHELL)) == NULL)
shellcmd = _PATH_CSHELL;
rc2 = run_command(shellcmd, NULL, nullfd, fileno(fbuf), "-c", cp + 1, NULL);
(void)close(nullfd);
if (rc2 < 0) {
(void)Fclose(fbuf);
break;
}
if (fsize(fbuf) == 0) {
(void)fprintf(stderr, "No bytes from command \"%s\"\n", cp + 1);
(void)Fclose(fbuf);
break;
}
rewind(fbuf);
}
else if (isdir(cp)) {
(void)printf("%s: Directory\n", cp);
break;
}
else if ((fbuf = Fopen(cp, "re")) == NULL) {
warn("%s", cp);
break;
}
(void)printf("\"%s\" ", cp);
(void)fflush(stdout);
lc = 0;
cc = 0;
while ((rc = readline(fbuf, linebuf, LINESIZE, 0)) >= 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);
(void)printf("%d/%d\n", lc, cc);
break;
case 'w':
cp = skip_WSP(&linebuf[2]);
if (*cp == '\0') {
(void)fprintf(stderr, "Write what file!?\n");
break;
}
if ((cp = expand(cp)) == NULL)
break;
rewind(collf);
(void)exwrite(cp, collf, 1);
break;
case 'm':
case 'M':
case 'f':
case 'F':
if (interpolate(linebuf + 2, collf, mailtempname, c) < 0)
goto err;
goto cont;
case '?':
cathelp(_PATH_TILDE);
break;
case 'p':
rewind(collf);
(void)printf("-------\nMessage contains:\n");
(void)puthead(hp, stdout,
GTO | GSUBJECT | GCC | GBCC | GSMOPTS | GNL);
while ((t = getc(collf)) != EOF)
(void)putchar(t);
goto cont;
case '|':
rewind(collf);
mespipe(collf, &linebuf[2]);
goto cont;
case 'v':
case 'e':
rewind(collf);
mesedit(collf, c);
goto cont;
}
}
goto out;
err:
if (collf != NULL) {
(void)Fclose(collf);
collf = NULL;
}
out:
if (collf != NULL)
rewind(collf);
noreset--;
sig_hold();
(void)sig_signal(SIGINT, old_sigint);
(void)sig_signal(SIGHUP, old_sighup);
(void)sig_signal(SIGTSTP, old_sigtstp);
(void)sig_signal(SIGTTIN, old_sigttin);
(void)sig_signal(SIGTTOU, old_sigttou);
sig_release();
sig_check();
return collf;
}