Annotation of src/usr.bin/mail/collect.c, Revision 1.4
1.4 ! deraadt 1: /* $OpenBSD: collect.c,v 1.3 1997/04/10 15:33:50 deraadt Exp $ */
1.2 deraadt 2: /* $NetBSD: collect.c,v 1.6 1996/06/08 19:48:16 christos Exp $ */
3:
1.1 deraadt 4: /*
5: * Copyright (c) 1980, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
1.2 deraadt 38: #if 0
39: static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
40: #else
1.4 ! deraadt 41: static char rcsid[] = "$OpenBSD: collect.c,v 1.3 1997/04/10 15:33:50 deraadt Exp $";
1.2 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: /*
46: * Mail -- a mail program
47: *
48: * Collect input from standard input, handling
49: * ~ escapes.
50: */
51:
52: #include "rcv.h"
53: #include "extern.h"
54:
55: /*
56: * Read a message from standard output and return a read file to it
57: * or NULL on error.
58: */
59:
60: /*
61: * The following hokiness with global variables is so that on
62: * receipt of an interrupt signal, the partial message can be salted
63: * away on dead.letter.
64: */
65:
66: static sig_t saveint; /* Previous SIGINT value */
67: static sig_t savehup; /* Previous SIGHUP value */
68: static sig_t savetstp; /* Previous SIGTSTP value */
69: static sig_t savettou; /* Previous SIGTTOU value */
70: static sig_t savettin; /* Previous SIGTTIN value */
71: static FILE *collf; /* File for saving away */
72: static int hadintr; /* Have seen one SIGINT so far */
73:
74: static jmp_buf colljmp; /* To get back to work */
75: static int colljmp_p; /* whether to long jump */
76: static jmp_buf collabort; /* To end collection with error */
77:
78: FILE *
79: collect(hp, printheaders)
80: struct header *hp;
81: int printheaders;
82: {
83: FILE *fbuf;
84: int lc, cc, escape, eofcount;
85: register int c, t;
86: char linebuf[LINESIZE], *cp;
87: extern char *tempMail;
88: char getsub;
1.3 deraadt 89: int omask;
1.2 deraadt 90: #if __GNUC__
91: /* Avoid longjmp clobbering */
92: (void) &escape;
93: (void) &eofcount;
94: (void) &getsub;
95: #endif
1.1 deraadt 96:
97: collf = NULL;
98: /*
99: * Start catching signals from here, but we're still die on interrupts
100: * until we're in the main loop.
101: */
1.3 deraadt 102: omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
1.1 deraadt 103: if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
104: signal(SIGINT, collint);
105: if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
106: signal(SIGHUP, collhup);
107: savetstp = signal(SIGTSTP, collstop);
108: savettou = signal(SIGTTOU, collstop);
109: savettin = signal(SIGTTIN, collstop);
110: if (setjmp(collabort) || setjmp(colljmp)) {
111: rm(tempMail);
112: goto err;
113: }
1.3 deraadt 114: sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
1.1 deraadt 115:
116: noreset++;
117: if ((collf = Fopen(tempMail, "w+")) == NULL) {
118: perror(tempMail);
119: goto err;
120: }
121: unlink(tempMail);
122:
123: /*
124: * If we are going to prompt for a subject,
125: * refrain from printing a newline after
126: * the headers (since some people mind).
127: */
128: t = GTO|GSUBJECT|GCC|GNL;
129: getsub = 0;
130: if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
131: (value("ask") != NOSTR || value("asksub") != NOSTR))
132: t &= ~GNL, getsub++;
133: if (printheaders) {
134: puthead(hp, stdout, t);
135: fflush(stdout);
136: }
137: if ((cp = value("escape")) != NOSTR)
138: escape = *cp;
139: else
140: escape = ESCAPE;
141: eofcount = 0;
142: hadintr = 0;
143:
144: if (!setjmp(colljmp)) {
145: if (getsub)
146: grabh(hp, GSUBJECT);
147: } else {
148: /*
149: * Come here for printing the after-signal message.
150: * Duplicate messages won't be printed because
151: * the write is aborted if we get a SIGTTOU.
152: */
153: cont:
154: if (hadintr) {
155: fflush(stdout);
156: fprintf(stderr,
157: "\n(Interrupt -- one more to kill letter)\n");
158: } else {
1.4 ! deraadt 159: if (isatty(0)) {
! 160: printf("(continue)\n");
! 161: fflush(stdout);
! 162: }
1.1 deraadt 163: }
164: }
165: for (;;) {
166: colljmp_p = 1;
167: c = readline(stdin, linebuf, LINESIZE);
168: colljmp_p = 0;
169: if (c < 0) {
170: if (value("interactive") != NOSTR &&
171: value("ignoreeof") != NOSTR && ++eofcount < 25) {
172: printf("Use \".\" to terminate letter\n");
173: continue;
174: }
175: break;
176: }
177: eofcount = 0;
178: hadintr = 0;
179: if (linebuf[0] == '.' && linebuf[1] == '\0' &&
180: value("interactive") != NOSTR &&
181: (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
182: break;
183: if (linebuf[0] != escape || value("interactive") == NOSTR) {
184: if (putline(collf, linebuf) < 0)
185: goto err;
186: continue;
187: }
188: c = linebuf[1];
189: switch (c) {
190: default:
191: /*
192: * On double escape, just send the single one.
193: * Otherwise, it's an error.
194: */
195: if (c == escape) {
196: if (putline(collf, &linebuf[1]) < 0)
197: goto err;
198: else
199: break;
200: }
201: printf("Unknown tilde escape.\n");
202: break;
203: case 'C':
204: /*
205: * Dump core.
206: */
1.2 deraadt 207: core(NULL);
1.1 deraadt 208: break;
209: case '!':
210: /*
211: * Shell escape, send the balance of the
212: * line to sh -c.
213: */
214: shell(&linebuf[2]);
215: break;
216: case ':':
217: case '_':
218: /*
219: * Escape to command mode, but be nice!
220: */
221: execute(&linebuf[2], 1);
222: goto cont;
223: case '.':
224: /*
225: * Simulate end of file on input.
226: */
227: goto out;
228: case 'q':
229: /*
230: * Force a quit of sending mail.
231: * Act like an interrupt happened.
232: */
233: hadintr++;
234: collint(SIGINT);
235: exit(1);
236: case 'h':
237: /*
238: * Grab a bunch of headers.
239: */
240: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
241: goto cont;
242: case 't':
243: /*
244: * Add to the To list.
245: */
246: hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
247: break;
248: case 's':
249: /*
250: * Set the Subject list.
251: */
252: cp = &linebuf[2];
253: while (isspace(*cp))
254: cp++;
255: hp->h_subject = savestr(cp);
256: break;
257: case 'c':
258: /*
259: * Add to the CC list.
260: */
261: hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
262: break;
263: case 'b':
264: /*
265: * Add stuff to blind carbon copies list.
266: */
267: hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
268: break;
269: case 'd':
270: strcpy(linebuf + 2, getdeadletter());
271: /* fall into . . . */
272: case 'r':
273: case '<':
274: /*
275: * Invoke a file:
276: * Search for the file name,
277: * then open it and copy the contents to collf.
278: */
279: cp = &linebuf[2];
280: while (isspace(*cp))
281: cp++;
282: if (*cp == '\0') {
283: printf("Interpolate what file?\n");
284: break;
285: }
286: cp = expand(cp);
287: if (cp == NOSTR)
288: break;
289: if (isdir(cp)) {
290: printf("%s: Directory\n", cp);
291: break;
292: }
293: if ((fbuf = Fopen(cp, "r")) == NULL) {
294: perror(cp);
295: break;
296: }
297: printf("\"%s\" ", cp);
298: fflush(stdout);
299: lc = 0;
300: cc = 0;
301: while (readline(fbuf, linebuf, LINESIZE) >= 0) {
302: lc++;
303: if ((t = putline(collf, linebuf)) < 0) {
304: Fclose(fbuf);
305: goto err;
306: }
307: cc += t;
308: }
309: Fclose(fbuf);
310: printf("%d/%d\n", lc, cc);
311: break;
312: case 'w':
313: /*
314: * Write the message on a file.
315: */
316: cp = &linebuf[2];
317: while (*cp == ' ' || *cp == '\t')
318: cp++;
319: if (*cp == '\0') {
320: fprintf(stderr, "Write what file!?\n");
321: break;
322: }
323: if ((cp = expand(cp)) == NOSTR)
324: break;
325: rewind(collf);
326: exwrite(cp, collf, 1);
327: break;
328: case 'm':
329: case 'M':
330: case 'f':
331: case 'F':
332: /*
333: * Interpolate the named messages, if we
334: * are in receiving mail mode. Does the
335: * standard list processing garbage.
336: * If ~f is given, we don't shift over.
337: */
338: if (forward(linebuf + 2, collf, c) < 0)
339: goto err;
340: goto cont;
341: case '?':
342: if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
343: perror(_PATH_TILDE);
344: break;
345: }
346: while ((t = getc(fbuf)) != EOF)
347: (void) putchar(t);
348: Fclose(fbuf);
349: break;
350: case 'p':
351: /*
352: * Print out the current state of the
353: * message without altering anything.
354: */
355: rewind(collf);
356: printf("-------\nMessage contains:\n");
357: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
358: while ((t = getc(collf)) != EOF)
359: (void) putchar(t);
360: goto cont;
361: case '|':
362: /*
363: * Pipe message through command.
364: * Collect output as new message.
365: */
366: rewind(collf);
367: mespipe(collf, &linebuf[2]);
368: goto cont;
369: case 'v':
370: case 'e':
371: /*
372: * Edit the current message.
373: * 'e' means to use EDITOR
374: * 'v' means to use VISUAL
375: */
376: rewind(collf);
377: mesedit(collf, c);
378: goto cont;
379: }
380: }
381: goto out;
382: err:
383: if (collf != NULL) {
384: Fclose(collf);
385: collf = NULL;
386: }
387: out:
388: if (collf != NULL)
389: rewind(collf);
390: noreset--;
1.3 deraadt 391: sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
1.1 deraadt 392: signal(SIGINT, saveint);
393: signal(SIGHUP, savehup);
394: signal(SIGTSTP, savetstp);
395: signal(SIGTTOU, savettou);
396: signal(SIGTTIN, savettin);
1.3 deraadt 397: sigsetmask(omask);
1.1 deraadt 398: return collf;
399: }
400:
401: /*
402: * Write a file, ex-like if f set.
403: */
404: int
405: exwrite(name, fp, f)
406: char name[];
407: FILE *fp;
408: int f;
409: {
410: register FILE *of;
411: register int c;
412: long cc;
413: int lc;
414: struct stat junk;
415:
416: if (f) {
417: printf("\"%s\" ", name);
418: fflush(stdout);
419: }
420: if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
421: if (!f)
422: fprintf(stderr, "%s: ", name);
423: fprintf(stderr, "File exists\n");
424: return(-1);
425: }
426: if ((of = Fopen(name, "w")) == NULL) {
427: perror(NOSTR);
428: return(-1);
429: }
430: lc = 0;
431: cc = 0;
432: while ((c = getc(fp)) != EOF) {
433: cc++;
434: if (c == '\n')
435: lc++;
436: (void) putc(c, of);
437: if (ferror(of)) {
438: perror(name);
439: Fclose(of);
440: return(-1);
441: }
442: }
443: Fclose(of);
444: printf("%d/%ld\n", lc, cc);
445: fflush(stdout);
446: return(0);
447: }
448:
449: /*
450: * Edit the message being collected on fp.
451: * On return, make the edit file the new temp file.
452: */
453: void
454: mesedit(fp, c)
455: FILE *fp;
456: int c;
457: {
458: sig_t sigint = signal(SIGINT, SIG_IGN);
459: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
460:
461: if (nf != NULL) {
462: fseek(nf, 0L, 2);
463: collf = nf;
464: Fclose(fp);
465: }
466: (void) signal(SIGINT, sigint);
467: }
468:
469: /*
470: * Pipe the message through the command.
471: * Old message is on stdin of command;
472: * New message collected from stdout.
473: * Sh -c must return 0 to accept the new message.
474: */
475: void
476: mespipe(fp, cmd)
477: FILE *fp;
478: char cmd[];
479: {
480: FILE *nf;
481: sig_t sigint = signal(SIGINT, SIG_IGN);
482: extern char *tempEdit;
483: char *shell;
484:
485: if ((nf = Fopen(tempEdit, "w+")) == NULL) {
486: perror(tempEdit);
487: goto out;
488: }
489: (void) unlink(tempEdit);
490: /*
491: * stdin = current message.
492: * stdout = new message.
493: */
494: if ((shell = value("SHELL")) == NOSTR)
495: shell = _PATH_CSHELL;
496: if (run_command(shell,
497: 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) {
498: (void) Fclose(nf);
499: goto out;
500: }
501: if (fsize(nf) == 0) {
502: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
503: (void) Fclose(nf);
504: goto out;
505: }
506: /*
507: * Take new files.
508: */
509: (void) fseek(nf, 0L, 2);
510: collf = nf;
511: (void) Fclose(fp);
512: out:
513: (void) signal(SIGINT, sigint);
514: }
515:
516: /*
517: * Interpolate the named messages into the current
518: * message, preceding each line with a tab.
519: * Return a count of the number of characters now in
520: * the message, or -1 if an error is encountered writing
521: * the message temporary. The flag argument is 'm' if we
522: * should shift over and 'f' if not.
523: */
524: int
525: forward(ms, fp, f)
526: char ms[];
527: FILE *fp;
528: int f;
529: {
530: register int *msgvec;
531: extern char *tempMail;
532: struct ignoretab *ig;
533: char *tabst;
534:
535: msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
536: if (msgvec == (int *) NOSTR)
537: return(0);
538: if (getmsglist(ms, msgvec, 0) < 0)
539: return(0);
540: if (*msgvec == 0) {
541: *msgvec = first(0, MMNORM);
542: if (*msgvec == NULL) {
543: printf("No appropriate messages\n");
544: return(0);
545: }
546: msgvec[1] = NULL;
547: }
548: if (f == 'f' || f == 'F')
549: tabst = NOSTR;
550: else if ((tabst = value("indentprefix")) == NOSTR)
551: tabst = "\t";
552: ig = isupper(f) ? NULL : ignore;
553: printf("Interpolating:");
554: for (; *msgvec != 0; msgvec++) {
555: struct message *mp = message + *msgvec - 1;
556:
557: touch(mp);
558: printf(" %d", *msgvec);
559: if (send(mp, fp, ig, tabst) < 0) {
560: perror(tempMail);
561: return(-1);
562: }
563: }
564: printf("\n");
565: return(0);
566: }
567:
568: /*
569: * Print (continue) when continued after ^Z.
570: */
571: /*ARGSUSED*/
572: void
573: collstop(s)
574: int s;
575: {
576: sig_t old_action = signal(s, SIG_DFL);
1.2 deraadt 577: sigset_t nset;
1.1 deraadt 578:
1.2 deraadt 579: sigemptyset(&nset);
580: sigaddset(&nset, s);
581: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 582: kill(0, s);
1.2 deraadt 583: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 584: signal(s, old_action);
585: if (colljmp_p) {
586: colljmp_p = 0;
587: hadintr = 0;
588: longjmp(colljmp, 1);
589: }
590: }
591:
592: /*
593: * On interrupt, come here to save the partial message in ~/dead.letter.
594: * Then jump out of the collection loop.
595: */
596: /*ARGSUSED*/
597: void
598: collint(s)
599: int s;
600: {
601: /*
602: * the control flow is subtle, because we can be called from ~q.
603: */
604: if (!hadintr) {
605: if (value("ignore") != NOSTR) {
606: puts("@");
607: fflush(stdout);
608: clearerr(stdin);
609: return;
610: }
611: hadintr = 1;
612: longjmp(colljmp, 1);
613: }
614: rewind(collf);
615: if (value("nosave") == NOSTR)
616: savedeadletter(collf);
617: longjmp(collabort, 1);
618: }
619:
620: /*ARGSUSED*/
621: void
622: collhup(s)
623: int s;
624: {
625: rewind(collf);
626: savedeadletter(collf);
627: /*
628: * Let's pretend nobody else wants to clean up,
629: * a true statement at this time.
630: */
631: exit(1);
632: }
633:
634: void
635: savedeadletter(fp)
636: register FILE *fp;
637: {
638: register FILE *dbuf;
639: register int c;
640: char *cp;
641:
642: if (fsize(fp) == 0)
643: return;
644: cp = getdeadletter();
645: c = umask(077);
646: dbuf = Fopen(cp, "a");
647: (void) umask(c);
648: if (dbuf == NULL)
649: return;
650: while ((c = getc(fp)) != EOF)
651: (void) putc(c, dbuf);
652: Fclose(dbuf);
653: rewind(fp);
654: }