1*4fe1ef32Schristos /* $NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $ */
288b833a7Schristos
361f28255Scgd /*
42cb5542fSderaadt * Copyright (c) 1980, 1993
52cb5542fSderaadt * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
1589aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3488b833a7Schristos #if 0
3588b833a7Schristos static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
3688b833a7Schristos #else
37*4fe1ef32Schristos __RCSID("$NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $");
3888b833a7Schristos #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd
4161f28255Scgd /*
4261f28255Scgd * Mail -- a mail program
4361f28255Scgd *
4461f28255Scgd * Collect input from standard input, handling
4561f28255Scgd * ~ escapes.
4661f28255Scgd */
4761f28255Scgd
48798fbc60Schristos #include <assert.h>
49798fbc60Schristos #include <util.h>
50798fbc60Schristos
5161f28255Scgd #include "rcv.h"
522cb5542fSderaadt #include "extern.h"
53798fbc60Schristos #include "format.h"
548207b28aSchristos #ifdef MIME_SUPPORT
558207b28aSchristos #include "mime.h"
568207b28aSchristos #endif
57ca13337dSchristos #include "sig.h"
58f3098750Schristos #include "thread.h"
5961f28255Scgd
6085ef0603Schristos
6161f28255Scgd /*
626e6e3265Swiz * Read a message from standard input and return a read file to it
6361f28255Scgd * or NULL on error.
6461f28255Scgd */
6561f28255Scgd
6661f28255Scgd /*
6761f28255Scgd * The following hokiness with global variables is so that on
6861f28255Scgd * receipt of an interrupt signal, the partial message can be salted
6961f28255Scgd * away on dead.letter.
7061f28255Scgd */
7161f28255Scgd static FILE *collf; /* File for saving away */
7261f28255Scgd static int hadintr; /* Have seen one SIGINT so far */
7361f28255Scgd
74ca13337dSchristos static jmp_buf abort_jmpbuf; /* To end collection with error */
75ca13337dSchristos static jmp_buf reset_jmpbuf; /* To get back to work */
76ca13337dSchristos static int reset_on_stop; /* To do job control longjmp. */
7785c81c58Schristos
78f3098750Schristos /*
79f3098750Schristos * Write a file, ex-like if f set.
80f3098750Schristos */
81f3098750Schristos static int
exwrite(const char name[],FILE * fp,int f)82f3098750Schristos exwrite(const char name[], FILE *fp, int f)
83f3098750Schristos {
84f3098750Schristos FILE *of;
85f3098750Schristos int c;
86f3098750Schristos long cc;
87f3098750Schristos int lc;
88f3098750Schristos struct stat junk;
89f3098750Schristos
90f3098750Schristos if (f) {
91f3098750Schristos (void)printf("\"%s\" ", name);
92f3098750Schristos (void)fflush(stdout);
93f3098750Schristos }
94f3098750Schristos if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) {
95f3098750Schristos if (!f)
96f3098750Schristos (void)fprintf(stderr, "%s: ", name);
97f3098750Schristos (void)fprintf(stderr, "File exists\n");
98f3098750Schristos return -1;
99f3098750Schristos }
1005942983dSchristos if ((of = Fopen(name, "we")) == NULL) {
101f3098750Schristos warn("%s", name);
102f3098750Schristos return -1;
103f3098750Schristos }
104f3098750Schristos lc = 0;
105f3098750Schristos cc = 0;
106f3098750Schristos while ((c = getc(fp)) != EOF) {
107f3098750Schristos cc++;
108f3098750Schristos if (c == '\n')
109f3098750Schristos lc++;
110f3098750Schristos (void)putc(c, of);
111f3098750Schristos if (ferror(of)) {
112f3098750Schristos warn("%s", name);
113f3098750Schristos (void)Fclose(of);
114f3098750Schristos return -1;
115f3098750Schristos }
116f3098750Schristos }
117f3098750Schristos (void)Fclose(of);
118f3098750Schristos (void)printf("%d/%ld\n", lc, cc);
119f3098750Schristos (void)fflush(stdout);
120f3098750Schristos return 0;
121f3098750Schristos }
122f3098750Schristos
123f3098750Schristos /*
124f3098750Schristos * Edit the message being collected on fp.
125f3098750Schristos * On return, make the edit file the new temp file.
126f3098750Schristos */
127f3098750Schristos static void
mesedit(FILE * fp,int c)128f3098750Schristos mesedit(FILE *fp, int c)
129f3098750Schristos {
130ca13337dSchristos struct sigaction osa;
131ca13337dSchristos sigset_t oset;
132ca13337dSchristos FILE *nf;
133f3098750Schristos
134ca13337dSchristos sig_check();
135ca13337dSchristos (void)sig_ignore(SIGINT, &osa, &oset);
136ca13337dSchristos nf = run_editor(fp, (off_t)-1, c, 0);
137f3098750Schristos if (nf != NULL) {
138f3098750Schristos (void)fseek(nf, 0L, 2);
139f3098750Schristos collf = nf;
140f3098750Schristos (void)Fclose(fp);
141f3098750Schristos }
142ca13337dSchristos (void)sig_restore(SIGINT, &osa, &oset);
143ca13337dSchristos sig_check();
144f3098750Schristos }
145f3098750Schristos
146f3098750Schristos /*
147f3098750Schristos * Pipe the message through the command.
148f3098750Schristos * Old message is on stdin of command;
149f3098750Schristos * New message collected from stdout.
150f3098750Schristos * Sh -c must return 0 to accept the new message.
151f3098750Schristos */
152f3098750Schristos static void
mespipe(FILE * fp,char cmd[])153f3098750Schristos mespipe(FILE *fp, char cmd[])
154f3098750Schristos {
155f3098750Schristos FILE *nf;
156ca13337dSchristos struct sigaction osa;
157ca13337dSchristos sigset_t oset;
158f3098750Schristos const char *shellcmd;
159f3098750Schristos int fd;
160f3098750Schristos char tempname[PATHSIZE];
161f3098750Schristos
162ca13337dSchristos sig_check();
163ca13337dSchristos (void)sig_ignore(SIGINT, &osa, &oset);
164ca13337dSchristos
165f3098750Schristos (void)snprintf(tempname, sizeof(tempname),
166f3098750Schristos "%s/mail.ReXXXXXXXXXX", tmpdir);
167f3098750Schristos if ((fd = mkstemp(tempname)) == -1 ||
168*4fe1ef32Schristos (nf = Fdopen(fd, "wef+")) == NULL) {
169f3098750Schristos if (fd != -1)
170f3098750Schristos (void)close(fd);
171f3098750Schristos warn("%s", tempname);
172f3098750Schristos goto out;
173f3098750Schristos }
174f3098750Schristos (void)unlink(tempname);
175f3098750Schristos /*
176f3098750Schristos * stdin = current message.
177f3098750Schristos * stdout = new message.
178f3098750Schristos */
179f3098750Schristos if ((shellcmd = value(ENAME_SHELL)) == NULL)
180f3098750Schristos shellcmd = _PATH_CSHELL;
181f3098750Schristos if (run_command(shellcmd,
182ca13337dSchristos NULL, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
183f3098750Schristos (void)Fclose(nf);
184f3098750Schristos goto out;
185f3098750Schristos }
186f3098750Schristos if (fsize(nf) == 0) {
187f3098750Schristos (void)fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
188f3098750Schristos (void)Fclose(nf);
189f3098750Schristos goto out;
190f3098750Schristos }
191f3098750Schristos /*
192f3098750Schristos * Take new files.
193f3098750Schristos */
194f3098750Schristos (void)fseek(nf, 0L, 2);
195f3098750Schristos collf = nf;
196f3098750Schristos (void)Fclose(fp);
197f3098750Schristos out:
198ca13337dSchristos (void)sig_restore(SIGINT, &osa, &oset);
199ca13337dSchristos sig_check();
200f3098750Schristos }
201f3098750Schristos
202f3098750Schristos /*
203f3098750Schristos * Interpolate the named messages into the current
204f3098750Schristos * message, preceding each line with a tab.
205f3098750Schristos * Return a count of the number of characters now in
206f3098750Schristos * the message, or -1 if an error is encountered writing
207f3098750Schristos * the message temporary. The flag argument is 'm' if we
208f3098750Schristos * should shift over and 'f' if not.
209f3098750Schristos */
210f3098750Schristos static int
interpolate(char ms[],FILE * fp,char * fn,int f)2114556f89aSchristos interpolate(char ms[], FILE *fp, char *fn, int f)
212f3098750Schristos {
213f3098750Schristos int *msgvec;
214f3098750Schristos struct ignoretab *ig;
215f3098750Schristos const char *tabst;
216f3098750Schristos #ifdef MIME_SUPPORT
217f3098750Schristos struct mime_info *mip;
218f3098750Schristos int retval;
219f3098750Schristos #endif
2202283d346Schristos msgvec = salloc((get_msgCount() + 1) * sizeof(*msgvec));
221f3098750Schristos if (msgvec == NULL)
222f3098750Schristos return 0;
223f3098750Schristos if (getmsglist(ms, msgvec, 0) < 0)
224f3098750Schristos return 0;
225f3098750Schristos if (*msgvec == 0) {
226f3098750Schristos *msgvec = first(0, MMNORM);
227f3098750Schristos if (*msgvec == 0) {
228f3098750Schristos (void)printf("No appropriate messages\n");
229f3098750Schristos return 0;
230f3098750Schristos }
231f3098750Schristos msgvec[1] = 0;
232f3098750Schristos }
233f3098750Schristos if (f == 'f' || f == 'F')
234f3098750Schristos tabst = NULL;
235f3098750Schristos else if ((tabst = value(ENAME_INDENTPREFIX)) == NULL)
236f3098750Schristos tabst = "\t";
237f3098750Schristos ig = isupper(f) ? NULL : ignore;
238f3098750Schristos (void)printf("Interpolating:");
239d727506fSchristos for (/*EMPTY*/; *msgvec != 0; msgvec++) {
240f3098750Schristos struct message *mp;
241f3098750Schristos char *fmtstr;
242f3098750Schristos
243f3098750Schristos mp = get_message(*msgvec);
244f3098750Schristos touch(mp);
245f3098750Schristos (void)printf(" %d", *msgvec);
246f3098750Schristos (void)fflush(stdout); /* flush stdout and the above */
247f3098750Schristos
248f3098750Schristos if (tabst && (fmtstr = value(ENAME_INDENT_PREAMBLE)) != NULL)
249f3098750Schristos fmsgprintf(collf, fmtstr, mp);
250f3098750Schristos #ifdef MIME_SUPPORT
251f3098750Schristos mip = NULL;
252f3098750Schristos if (value(ENAME_MIME_DECODE_MSG)) {
253f3098750Schristos if ((tabst == NULL && value(ENAME_MIME_DECODE_INSERT)) ||
254f3098750Schristos (tabst != NULL && value(ENAME_MIME_DECODE_QUOTE)))
255f3098750Schristos mip = mime_decode_open(mp);
256f3098750Schristos }
257f3098750Schristos retval = mime_sendmessage(mp, fp, ig, tabst, mip);
258f3098750Schristos mime_decode_close(mip);
259f3098750Schristos if (retval < 0)
260f3098750Schristos #else
261f3098750Schristos if (sendmessage(mp, fp, ig, tabst, NULL) < 0)
262f3098750Schristos #endif
263f3098750Schristos {
264f3098750Schristos warn("%s", fn);
265f3098750Schristos return -1;
266f3098750Schristos }
267f3098750Schristos if (tabst && (fmtstr = value(ENAME_INDENT_POSTSCRIPT)) != NULL)
268f3098750Schristos fmsgprintf(collf, fmtstr, mp);
269f3098750Schristos }
270f3098750Schristos (void)printf("\n");
271f3098750Schristos return 0;
272f3098750Schristos }
273f3098750Schristos
274f3098750Schristos /*
275f3098750Schristos * Append the contents of the file to the end of the deadletter file.
276f3098750Schristos */
277f3098750Schristos PUBLIC void
savedeadletter(FILE * fp)278f3098750Schristos savedeadletter(FILE *fp)
279f3098750Schristos {
280f3098750Schristos FILE *dbuf;
281f3098750Schristos mode_t m;
282f3098750Schristos int c;
283f3098750Schristos const char *cp;
284f3098750Schristos
285f3098750Schristos if (fsize(fp) == 0)
286f3098750Schristos return;
287f3098750Schristos cp = getdeadletter();
288f3098750Schristos m = umask(077);
2895942983dSchristos dbuf = Fopen(cp, "ae");
290f3098750Schristos (void)umask(m);
291f3098750Schristos if (dbuf == NULL)
292f3098750Schristos return;
293ca13337dSchristos (void)printf("Saving message body to `%s'.\n", cp);
294f3098750Schristos while ((c = getc(fp)) != EOF)
295f3098750Schristos (void)putc(c, dbuf);
296f3098750Schristos (void)Fclose(dbuf);
297f3098750Schristos rewind(fp);
298f3098750Schristos }
299f3098750Schristos
300f3098750Schristos /*
301f3098750Schristos * On interrupt, come here to save the partial message in ~/dead.letter.
302f3098750Schristos * Then jump out of the collection loop.
303f3098750Schristos */
304f3098750Schristos static void
coll_int(int signo)305ca13337dSchristos coll_int(int signo)
306f3098750Schristos {
3078e33b891Schristos sig_t o = signal(SIGINT, SIG_IGN);
308ca13337dSchristos
309f3098750Schristos /*
310f3098750Schristos * the control flow is subtle, because we can be called from ~q.
311f3098750Schristos */
312f3098750Schristos if (!hadintr) {
313f3098750Schristos if (value(ENAME_IGNORE) != NULL) {
314f3098750Schristos (void)puts("@");
315f3098750Schristos (void)fflush(stdout);
316f3098750Schristos clearerr(stdin);
3173cc72f88Schristos signal(SIGINT, o);
318f3098750Schristos return;
319f3098750Schristos }
320f3098750Schristos hadintr = 1;
3213cc72f88Schristos signal(SIGINT, o);
322ca13337dSchristos longjmp(reset_jmpbuf, signo);
323f3098750Schristos }
3248e33b891Schristos if (collf) {
325f3098750Schristos rewind(collf);
326f3098750Schristos if (value(ENAME_NOSAVE) == NULL)
327f3098750Schristos savedeadletter(collf);
3288e33b891Schristos }
3298e33b891Schristos signal(SIGINT, o);
330ca13337dSchristos longjmp(abort_jmpbuf, signo);
331f3098750Schristos }
332f3098750Schristos
333f3098750Schristos /*ARGSUSED*/
3346818646aSjoerg __dead static void
coll_hup(int signo __unused)335ca13337dSchristos coll_hup(int signo __unused)
336f3098750Schristos {
337ca13337dSchristos
338f3098750Schristos rewind(collf);
339f3098750Schristos savedeadletter(collf);
340f3098750Schristos /*
341f3098750Schristos * Let's pretend nobody else wants to clean up,
342f3098750Schristos * a true statement at this time.
343f3098750Schristos */
344ca13337dSchristos exit(EXIT_FAILURE);
345ca13337dSchristos }
346ca13337dSchristos
347ca13337dSchristos /*
348ca13337dSchristos * Print (continue) when continued after ^Z.
349ca13337dSchristos */
350ca13337dSchristos static void
coll_stop(int signo)351ca13337dSchristos coll_stop(int signo)
352ca13337dSchristos {
353ca13337dSchristos
354ca13337dSchristos if (reset_on_stop) {
355ca13337dSchristos reset_on_stop = 0;
356ca13337dSchristos hadintr = 0;
357ca13337dSchristos longjmp(reset_jmpbuf, signo);
358ca13337dSchristos }
359f3098750Schristos }
360f3098750Schristos
361f3098750Schristos PUBLIC FILE *
collect(struct header * hp,int printheaders)362b127ccccSwiz collect(struct header *hp, int printheaders)
36361f28255Scgd {
364a7879b44Schristos sig_t volatile old_sigint = sig_current(SIGINT);
365a7879b44Schristos sig_t volatile old_sighup = sig_current(SIGHUP);
366a7879b44Schristos sig_t volatile old_sigtstp = sig_current(SIGTSTP);
367a7879b44Schristos sig_t volatile old_sigttin = sig_current(SIGTTIN);
368a7879b44Schristos sig_t volatile old_sigttou = sig_current(SIGTTOU);
36961f28255Scgd FILE *fbuf;
370f0f6b1cdSchristos int lc, cc;
371443084c8Swiz int c, fd, t;
372ece0fd5cSchristos char linebuf[LINESIZE];
373ece0fd5cSchristos const char *cp;
374443084c8Swiz char tempname[PATHSIZE];
375240d8221Swiz char mailtempname[PATHSIZE];
3768207b28aSchristos int eofcount;
3778207b28aSchristos int longline;
378ca13337dSchristos int lastlong, rc; /* So we don't make 2 or more lines
379ca13337dSchristos out of a long input line. */
380f0f6b1cdSchristos
381f0f6b1cdSchristos /* The following are declared volatile to avoid longjmp clobbering. */
3828207b28aSchristos char volatile getsub;
3838207b28aSchristos int volatile escape;
38461f28255Scgd
385ca286310Schristos (void)memset(mailtempname, 0, sizeof(mailtempname));
38661f28255Scgd collf = NULL;
387ca13337dSchristos
388ca13337dSchristos if (setjmp(abort_jmpbuf) || setjmp(reset_jmpbuf)) {
389240d8221Swiz (void)rm(mailtempname);
39061f28255Scgd goto err;
39161f28255Scgd }
392ca13337dSchristos sig_check();
393ca13337dSchristos
394ca13337dSchristos sig_hold();
395ca13337dSchristos old_sigint = sig_signal(SIGINT, coll_int);
396ca13337dSchristos old_sighup = sig_signal(SIGHUP, coll_hup);
397ca13337dSchristos old_sigtstp = sig_signal(SIGTSTP, coll_stop);
398ca13337dSchristos old_sigttin = sig_signal(SIGTTIN, coll_stop);
399ca13337dSchristos old_sigttou = sig_signal(SIGTTOU, coll_stop);
400ca13337dSchristos sig_release();
40161f28255Scgd
40261f28255Scgd noreset++;
403240d8221Swiz (void)snprintf(mailtempname, sizeof(mailtempname),
404240d8221Swiz "%s/mail.RsXXXXXXXXXX", tmpdir);
405240d8221Swiz if ((fd = mkstemp(mailtempname)) == -1 ||
406*4fe1ef32Schristos (collf = Fdopen(fd, "wef+")) == NULL) {
407240d8221Swiz if (fd != -1)
408ca286310Schristos (void)close(fd);
409240d8221Swiz warn("%s", mailtempname);
41061f28255Scgd goto err;
41161f28255Scgd }
412240d8221Swiz (void)rm(mailtempname);
41361f28255Scgd
41461f28255Scgd /*
41561f28255Scgd * If we are going to prompt for a subject,
41661f28255Scgd * refrain from printing a newline after
41761f28255Scgd * the headers (since some people mind).
41861f28255Scgd */
41985c81c58Schristos t = GTO | GSUBJECT | GCC | GNL | GSMOPTS;
42061f28255Scgd getsub = 0;
421f3098750Schristos if (hp->h_subject == NULL && value(ENAME_INTERACTIVE) != NULL &&
422ca13337dSchristos (value(ENAME_ASK) != NULL || value(ENAME_ASKSUB) != NULL)) {
423ca13337dSchristos t &= ~GNL;
424ca13337dSchristos getsub++;
425ca13337dSchristos }
42661f28255Scgd if (printheaders) {
427ca286310Schristos (void)puthead(hp, stdout, t);
428ca286310Schristos (void)fflush(stdout);
42961f28255Scgd }
430f3098750Schristos if ((cp = value(ENAME_ESCAPE)) != NULL)
43161f28255Scgd escape = *cp;
43261f28255Scgd else
43361f28255Scgd escape = ESCAPE;
434ca13337dSchristos hadintr = 0;
435ca13337dSchristos if (setjmp(reset_jmpbuf) == 0) {
43661f28255Scgd if (getsub)
437ca286310Schristos (void)grabh(hp, GSUBJECT);
43861f28255Scgd } else {
43961f28255Scgd /*
44061f28255Scgd * Come here for printing the after-signal message.
44161f28255Scgd * Duplicate messages won't be printed because
44261f28255Scgd * the write is aborted if we get a SIGTTOU.
44361f28255Scgd */
44461f28255Scgd cont:
44561f28255Scgd if (hadintr) {
446ca286310Schristos (void)fflush(stdout);
447ca286310Schristos (void)fprintf(stderr,
44861f28255Scgd "\n(Interrupt -- one more to kill letter)\n");
44961f28255Scgd } else {
450ca286310Schristos (void)printf("(continue)\n");
451ca286310Schristos (void)fflush(stdout);
45261f28255Scgd }
45361f28255Scgd }
4548207b28aSchristos eofcount = 0; /* reset after possible longjmp */
4558207b28aSchristos longline = 0; /* reset after possible longjmp */
45661f28255Scgd for (;;) {
457ca13337dSchristos reset_on_stop = 1;
458ca13337dSchristos c = readline(stdin, linebuf, LINESIZE, reset_on_stop);
459ca13337dSchristos reset_on_stop = 0;
460ca13337dSchristos
46185c81c58Schristos if (c < 0) {
46285c81c58Schristos char *p;
463ca13337dSchristos
464f3098750Schristos if (value(ENAME_INTERACTIVE) != NULL &&
465f3098750Schristos (p = value(ENAME_IGNOREEOF)) != NULL &&
46685c81c58Schristos ++eofcount < (*p == 0 ? 25 : atoi(p))) {
46785c81c58Schristos (void)printf("Use \".\" to terminate letter\n");
46885c81c58Schristos continue;
46985c81c58Schristos }
47085c81c58Schristos break;
47185c81c58Schristos }
472a8316bbcSphil lastlong = longline;
473a8316bbcSphil longline = c == LINESIZE - 1;
47461f28255Scgd eofcount = 0;
47561f28255Scgd hadintr = 0;
47661f28255Scgd if (linebuf[0] == '.' && linebuf[1] == '\0' &&
477f3098750Schristos value(ENAME_INTERACTIVE) != NULL && !lastlong &&
478f3098750Schristos (value(ENAME_DOT) != NULL || value(ENAME_IGNOREEOF) != NULL))
47961f28255Scgd break;
480f3098750Schristos if (linebuf[0] != escape || value(ENAME_INTERACTIVE) == NULL ||
481a8316bbcSphil lastlong) {
482a8316bbcSphil if (putline(collf, linebuf, !longline) < 0)
48361f28255Scgd goto err;
48461f28255Scgd continue;
48561f28255Scgd }
48661f28255Scgd c = linebuf[1];
48761f28255Scgd switch (c) {
48861f28255Scgd default:
48961f28255Scgd /*
49061f28255Scgd * On double escape, just send the single one.
49161f28255Scgd * Otherwise, it's an error.
49261f28255Scgd */
49361f28255Scgd if (c == escape) {
494a8316bbcSphil if (putline(collf, &linebuf[1], !longline) < 0)
49561f28255Scgd goto err;
49661f28255Scgd else
49761f28255Scgd break;
49861f28255Scgd }
499ca286310Schristos (void)printf("Unknown tilde escape.\n");
50061f28255Scgd break;
5018207b28aSchristos #ifdef MIME_SUPPORT
5028207b28aSchristos case '@':
503798fbc60Schristos hp->h_attach = mime_attach_files(hp->h_attach, &linebuf[2]);
5048207b28aSchristos break;
5058207b28aSchristos #endif
50661f28255Scgd case 'C':
50761f28255Scgd /*
50861f28255Scgd * Dump core.
50961f28255Scgd */
510ca286310Schristos (void)core(NULL);
51161f28255Scgd break;
51261f28255Scgd case '!':
51361f28255Scgd /*
51461f28255Scgd * Shell escape, send the balance of the
51561f28255Scgd * line to sh -c.
51661f28255Scgd */
517ca286310Schristos (void)shell(&linebuf[2]);
51861f28255Scgd break;
51961f28255Scgd case ':':
520a067af72Sjtc case '_':
52161f28255Scgd /*
52261f28255Scgd * Escape to command mode, but be nice!
52361f28255Scgd */
5248d6767f0Schristos (void)execute(&linebuf[2], ec_composing);
52561f28255Scgd goto cont;
52661f28255Scgd case '.':
52761f28255Scgd /*
52861f28255Scgd * Simulate end of file on input.
52961f28255Scgd */
53061f28255Scgd goto out;
53161f28255Scgd case 'q':
53261f28255Scgd /*
53361f28255Scgd * Force a quit of sending mail.
53461f28255Scgd * Act like an interrupt happened.
53561f28255Scgd */
53661f28255Scgd hadintr++;
537ca13337dSchristos coll_int(SIGINT);
53861f28255Scgd exit(1);
539ca286310Schristos /*NOTREACHED*/
540fb60b363Smjl
541fb60b363Smjl case 'x': /* exit, do not save in dead.letter */
542fb60b363Smjl goto err;
543fb60b363Smjl
54461f28255Scgd case 'h':
54561f28255Scgd /*
54661f28255Scgd * Grab a bunch of headers.
54761f28255Scgd */
54885c81c58Schristos (void)grabh(hp, GTO | GSUBJECT | GCC | GBCC | GSMOPTS);
54961f28255Scgd goto cont;
55061f28255Scgd case 't':
55161f28255Scgd /*
55261f28255Scgd * Add to the To list.
55361f28255Scgd */
55461f28255Scgd hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
55561f28255Scgd break;
55661f28255Scgd case 's':
55761f28255Scgd /*
55861f28255Scgd * Set the Subject list.
55961f28255Scgd */
560d727506fSchristos cp = skip_WSP(&linebuf[2]);
56161f28255Scgd hp->h_subject = savestr(cp);
56261f28255Scgd break;
56361f28255Scgd case 'c':
56461f28255Scgd /*
56561f28255Scgd * Add to the CC list.
56661f28255Scgd */
56761f28255Scgd hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
56861f28255Scgd break;
56961f28255Scgd case 'b':
57061f28255Scgd /*
57161f28255Scgd * Add stuff to blind carbon copies list.
57261f28255Scgd */
57361f28255Scgd hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
57461f28255Scgd break;
575fb60b363Smjl case 'i':
576fb60b363Smjl case 'A':
577fb60b363Smjl case 'a':
578fb60b363Smjl /*
579fb60b363Smjl * Insert named variable in message
580fb60b363Smjl */
581fb60b363Smjl
582fb60b363Smjl switch(c) {
583fb60b363Smjl case 'i':
584d727506fSchristos cp = skip_WSP(&linebuf[2]);
585fb60b363Smjl break;
586fb60b363Smjl case 'a':
587fb60b363Smjl cp = "sign";
588fb60b363Smjl break;
589fb60b363Smjl case 'A':
590fb60b363Smjl cp = "Sign";
591fb60b363Smjl break;
592fb60b363Smjl default:
593fb60b363Smjl goto err;
594fb60b363Smjl }
595fb60b363Smjl
596ab850155Swiz if (*cp && (cp = value(cp)) != NULL) {
597ca286310Schristos (void)printf("%s\n", cp);
598fb60b363Smjl if (putline(collf, cp, 1) < 0)
599fb60b363Smjl goto err;
600fb60b363Smjl }
601fb60b363Smjl
602fb60b363Smjl break;
603fb60b363Smjl
60461f28255Scgd case 'd':
605ca286310Schristos (void)strcpy(linebuf + 2, getdeadletter());
606ca286310Schristos /* FALLTHROUGH */
60761f28255Scgd case 'r':
608a067af72Sjtc case '<':
60961f28255Scgd /*
61061f28255Scgd * Invoke a file:
61161f28255Scgd * Search for the file name,
61261f28255Scgd * then open it and copy the contents to collf.
61361f28255Scgd */
614d727506fSchristos cp = skip_WSP(&linebuf[2]);
61561f28255Scgd if (*cp == '\0') {
616ca286310Schristos (void)printf("Interpolate what file?\n");
61761f28255Scgd break;
61861f28255Scgd }
619fb60b363Smjl
62061f28255Scgd cp = expand(cp);
621ab850155Swiz if (cp == NULL)
62261f28255Scgd break;
623fb60b363Smjl
624fb60b363Smjl if (*cp == '!') { /* insert stdout of command */
625ece0fd5cSchristos const char *shellcmd;
626fb60b363Smjl int nullfd;
6274e972651Swiz int rc2;
628fb60b363Smjl
629fb60b363Smjl if ((nullfd = open("/dev/null", O_RDONLY, 0)) == -1) {
630ae38aa87Swiz warn("/dev/null");
631fb60b363Smjl break;
632fb60b363Smjl }
633fb60b363Smjl
634443084c8Swiz (void)snprintf(tempname, sizeof(tempname),
635240d8221Swiz "%s/mail.ReXXXXXXXXXX", tmpdir);
636443084c8Swiz if ((fd = mkstemp(tempname)) == -1 ||
637*4fe1ef32Schristos (fbuf = Fdopen(fd, "wef+")) == NULL) {
638443084c8Swiz if (fd != -1)
639ca286310Schristos (void)close(fd);
640443084c8Swiz warn("%s", tempname);
641fb60b363Smjl break;
642fb60b363Smjl }
643443084c8Swiz (void)unlink(tempname);
644fb60b363Smjl
645f3098750Schristos if ((shellcmd = value(ENAME_SHELL)) == NULL)
6464e972651Swiz shellcmd = _PATH_CSHELL;
647fb60b363Smjl
648ca13337dSchristos rc2 = run_command(shellcmd, NULL, nullfd, fileno(fbuf), "-c", cp + 1, NULL);
649fb60b363Smjl
650ca286310Schristos (void)close(nullfd);
651fb60b363Smjl
6524e972651Swiz if (rc2 < 0) {
653fb60b363Smjl (void)Fclose(fbuf);
654fb60b363Smjl break;
655fb60b363Smjl }
656fb60b363Smjl
657fb60b363Smjl if (fsize(fbuf) == 0) {
658ca286310Schristos (void)fprintf(stderr, "No bytes from command \"%s\"\n", cp + 1);
659fb60b363Smjl (void)Fclose(fbuf);
660fb60b363Smjl break;
661fb60b363Smjl }
662fb60b363Smjl
663fb60b363Smjl rewind(fbuf);
664fb60b363Smjl }
665fb60b363Smjl else if (isdir(cp)) {
666ca286310Schristos (void)printf("%s: Directory\n", cp);
66761f28255Scgd break;
66861f28255Scgd }
6695942983dSchristos else if ((fbuf = Fopen(cp, "re")) == NULL) {
670ae38aa87Swiz warn("%s", cp);
67161f28255Scgd break;
67261f28255Scgd }
673ca286310Schristos (void)printf("\"%s\" ", cp);
674ca286310Schristos (void)fflush(stdout);
67561f28255Scgd lc = 0;
67661f28255Scgd cc = 0;
677ca13337dSchristos while ((rc = readline(fbuf, linebuf, LINESIZE, 0)) >= 0) {
678a8316bbcSphil if (rc != LINESIZE-1) lc++;
679a8316bbcSphil if ((t = putline(collf, linebuf,
680a8316bbcSphil rc != LINESIZE-1)) < 0) {
681ca286310Schristos (void)Fclose(fbuf);
68261f28255Scgd goto err;
68361f28255Scgd }
68461f28255Scgd cc += t;
68561f28255Scgd }
686ca286310Schristos (void)Fclose(fbuf);
687ca286310Schristos (void)printf("%d/%d\n", lc, cc);
68861f28255Scgd break;
68961f28255Scgd case 'w':
69061f28255Scgd /*
69161f28255Scgd * Write the message on a file.
69261f28255Scgd */
693d727506fSchristos cp = skip_WSP(&linebuf[2]);
69461f28255Scgd if (*cp == '\0') {
695ca286310Schristos (void)fprintf(stderr, "Write what file!?\n");
69661f28255Scgd break;
69761f28255Scgd }
698ab850155Swiz if ((cp = expand(cp)) == NULL)
69961f28255Scgd break;
70061f28255Scgd rewind(collf);
701ca286310Schristos (void)exwrite(cp, collf, 1);
70261f28255Scgd break;
70361f28255Scgd case 'm':
70461f28255Scgd case 'M':
70561f28255Scgd case 'f':
70661f28255Scgd case 'F':
70761f28255Scgd /*
70861f28255Scgd * Interpolate the named messages, if we
70961f28255Scgd * are in receiving mail mode. Does the
71061f28255Scgd * standard list processing garbage.
71161f28255Scgd * If ~f is given, we don't shift over.
71261f28255Scgd */
7134556f89aSchristos if (interpolate(linebuf + 2, collf, mailtempname, c) < 0)
71461f28255Scgd goto err;
71561f28255Scgd goto cont;
71661f28255Scgd case '?':
71741101b58Schristos cathelp(_PATH_TILDE);
71861f28255Scgd break;
71961f28255Scgd case 'p':
72061f28255Scgd /*
72161f28255Scgd * Print out the current state of the
72261f28255Scgd * message without altering anything.
72361f28255Scgd */
72461f28255Scgd rewind(collf);
725ca286310Schristos (void)printf("-------\nMessage contains:\n");
726ca13337dSchristos (void)puthead(hp, stdout,
727ca13337dSchristos GTO | GSUBJECT | GCC | GBCC | GSMOPTS | GNL);
72861f28255Scgd while ((t = getc(collf)) != EOF)
72961f28255Scgd (void)putchar(t);
73061f28255Scgd goto cont;
73161f28255Scgd case '|':
73261f28255Scgd /*
73361f28255Scgd * Pipe message through command.
73461f28255Scgd * Collect output as new message.
73561f28255Scgd */
73661f28255Scgd rewind(collf);
73761f28255Scgd mespipe(collf, &linebuf[2]);
73861f28255Scgd goto cont;
73961f28255Scgd case 'v':
74061f28255Scgd case 'e':
74161f28255Scgd /*
74261f28255Scgd * Edit the current message.
74361f28255Scgd * 'e' means to use EDITOR
74461f28255Scgd * 'v' means to use VISUAL
74561f28255Scgd */
74661f28255Scgd rewind(collf);
74761f28255Scgd mesedit(collf, c);
74861f28255Scgd goto cont;
74961f28255Scgd }
75061f28255Scgd }
75161f28255Scgd goto out;
75261f28255Scgd err:
75361f28255Scgd if (collf != NULL) {
754ca286310Schristos (void)Fclose(collf);
75561f28255Scgd collf = NULL;
75661f28255Scgd }
75761f28255Scgd out:
75861f28255Scgd if (collf != NULL)
75961f28255Scgd rewind(collf);
76061f28255Scgd noreset--;
761ca13337dSchristos
762ca13337dSchristos sig_hold();
763ca13337dSchristos (void)sig_signal(SIGINT, old_sigint);
764ca13337dSchristos (void)sig_signal(SIGHUP, old_sighup);
765ca13337dSchristos (void)sig_signal(SIGTSTP, old_sigtstp);
766ca13337dSchristos (void)sig_signal(SIGTTIN, old_sigttin);
767ca13337dSchristos (void)sig_signal(SIGTTOU, old_sigttou);
768ca13337dSchristos sig_release();
769ca13337dSchristos
770ca13337dSchristos sig_check();
77161f28255Scgd return collf;
77261f28255Scgd }
773