Annotation of src/usr.bin/mail/collect.c, Revision 1.2
1.2 ! deraadt 1: /* $OpenBSD: collect.c,v 1.6 1996/06/08 19:48:16 christos Exp $ */
! 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
! 41: static char rcsid[] = "$OpenBSD: collect.c,v 1.6 1996/06/08 19:48:16 christos Exp $";
! 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.2 ! deraadt 89: sigset_t oset, nset;
! 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.2 ! deraadt 102: sigemptyset(&nset);
! 103: sigaddset(&nset, SIGINT);
! 104: sigaddset(&nset, SIGHUP);
! 105: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 106: if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
107: signal(SIGINT, collint);
108: if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
109: signal(SIGHUP, collhup);
110: savetstp = signal(SIGTSTP, collstop);
111: savettou = signal(SIGTTOU, collstop);
112: savettin = signal(SIGTTIN, collstop);
113: if (setjmp(collabort) || setjmp(colljmp)) {
114: rm(tempMail);
115: goto err;
116: }
1.2 ! deraadt 117: sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 deraadt 118:
119: noreset++;
120: if ((collf = Fopen(tempMail, "w+")) == NULL) {
121: perror(tempMail);
122: goto err;
123: }
124: unlink(tempMail);
125:
126: /*
127: * If we are going to prompt for a subject,
128: * refrain from printing a newline after
129: * the headers (since some people mind).
130: */
131: t = GTO|GSUBJECT|GCC|GNL;
132: getsub = 0;
133: if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
134: (value("ask") != NOSTR || value("asksub") != NOSTR))
135: t &= ~GNL, getsub++;
136: if (printheaders) {
137: puthead(hp, stdout, t);
138: fflush(stdout);
139: }
140: if ((cp = value("escape")) != NOSTR)
141: escape = *cp;
142: else
143: escape = ESCAPE;
144: eofcount = 0;
145: hadintr = 0;
146:
147: if (!setjmp(colljmp)) {
148: if (getsub)
149: grabh(hp, GSUBJECT);
150: } else {
151: /*
152: * Come here for printing the after-signal message.
153: * Duplicate messages won't be printed because
154: * the write is aborted if we get a SIGTTOU.
155: */
156: cont:
157: if (hadintr) {
158: fflush(stdout);
159: fprintf(stderr,
160: "\n(Interrupt -- one more to kill letter)\n");
161: } else {
162: printf("(continue)\n");
163: fflush(stdout);
164: }
165: }
166: for (;;) {
167: colljmp_p = 1;
168: c = readline(stdin, linebuf, LINESIZE);
169: colljmp_p = 0;
170: if (c < 0) {
171: if (value("interactive") != NOSTR &&
172: value("ignoreeof") != NOSTR && ++eofcount < 25) {
173: printf("Use \".\" to terminate letter\n");
174: continue;
175: }
176: break;
177: }
178: eofcount = 0;
179: hadintr = 0;
180: if (linebuf[0] == '.' && linebuf[1] == '\0' &&
181: value("interactive") != NOSTR &&
182: (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
183: break;
184: if (linebuf[0] != escape || value("interactive") == NOSTR) {
185: if (putline(collf, linebuf) < 0)
186: goto err;
187: continue;
188: }
189: c = linebuf[1];
190: switch (c) {
191: default:
192: /*
193: * On double escape, just send the single one.
194: * Otherwise, it's an error.
195: */
196: if (c == escape) {
197: if (putline(collf, &linebuf[1]) < 0)
198: goto err;
199: else
200: break;
201: }
202: printf("Unknown tilde escape.\n");
203: break;
204: case 'C':
205: /*
206: * Dump core.
207: */
1.2 ! deraadt 208: core(NULL);
1.1 deraadt 209: break;
210: case '!':
211: /*
212: * Shell escape, send the balance of the
213: * line to sh -c.
214: */
215: shell(&linebuf[2]);
216: break;
217: case ':':
218: case '_':
219: /*
220: * Escape to command mode, but be nice!
221: */
222: execute(&linebuf[2], 1);
223: goto cont;
224: case '.':
225: /*
226: * Simulate end of file on input.
227: */
228: goto out;
229: case 'q':
230: /*
231: * Force a quit of sending mail.
232: * Act like an interrupt happened.
233: */
234: hadintr++;
235: collint(SIGINT);
236: exit(1);
237: case 'h':
238: /*
239: * Grab a bunch of headers.
240: */
241: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
242: goto cont;
243: case 't':
244: /*
245: * Add to the To list.
246: */
247: hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
248: break;
249: case 's':
250: /*
251: * Set the Subject list.
252: */
253: cp = &linebuf[2];
254: while (isspace(*cp))
255: cp++;
256: hp->h_subject = savestr(cp);
257: break;
258: case 'c':
259: /*
260: * Add to the CC list.
261: */
262: hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
263: break;
264: case 'b':
265: /*
266: * Add stuff to blind carbon copies list.
267: */
268: hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
269: break;
270: case 'd':
271: strcpy(linebuf + 2, getdeadletter());
272: /* fall into . . . */
273: case 'r':
274: case '<':
275: /*
276: * Invoke a file:
277: * Search for the file name,
278: * then open it and copy the contents to collf.
279: */
280: cp = &linebuf[2];
281: while (isspace(*cp))
282: cp++;
283: if (*cp == '\0') {
284: printf("Interpolate what file?\n");
285: break;
286: }
287: cp = expand(cp);
288: if (cp == NOSTR)
289: break;
290: if (isdir(cp)) {
291: printf("%s: Directory\n", cp);
292: break;
293: }
294: if ((fbuf = Fopen(cp, "r")) == NULL) {
295: perror(cp);
296: break;
297: }
298: printf("\"%s\" ", cp);
299: fflush(stdout);
300: lc = 0;
301: cc = 0;
302: while (readline(fbuf, linebuf, LINESIZE) >= 0) {
303: lc++;
304: if ((t = putline(collf, linebuf)) < 0) {
305: Fclose(fbuf);
306: goto err;
307: }
308: cc += t;
309: }
310: Fclose(fbuf);
311: printf("%d/%d\n", lc, cc);
312: break;
313: case 'w':
314: /*
315: * Write the message on a file.
316: */
317: cp = &linebuf[2];
318: while (*cp == ' ' || *cp == '\t')
319: cp++;
320: if (*cp == '\0') {
321: fprintf(stderr, "Write what file!?\n");
322: break;
323: }
324: if ((cp = expand(cp)) == NOSTR)
325: break;
326: rewind(collf);
327: exwrite(cp, collf, 1);
328: break;
329: case 'm':
330: case 'M':
331: case 'f':
332: case 'F':
333: /*
334: * Interpolate the named messages, if we
335: * are in receiving mail mode. Does the
336: * standard list processing garbage.
337: * If ~f is given, we don't shift over.
338: */
339: if (forward(linebuf + 2, collf, c) < 0)
340: goto err;
341: goto cont;
342: case '?':
343: if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
344: perror(_PATH_TILDE);
345: break;
346: }
347: while ((t = getc(fbuf)) != EOF)
348: (void) putchar(t);
349: Fclose(fbuf);
350: break;
351: case 'p':
352: /*
353: * Print out the current state of the
354: * message without altering anything.
355: */
356: rewind(collf);
357: printf("-------\nMessage contains:\n");
358: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
359: while ((t = getc(collf)) != EOF)
360: (void) putchar(t);
361: goto cont;
362: case '|':
363: /*
364: * Pipe message through command.
365: * Collect output as new message.
366: */
367: rewind(collf);
368: mespipe(collf, &linebuf[2]);
369: goto cont;
370: case 'v':
371: case 'e':
372: /*
373: * Edit the current message.
374: * 'e' means to use EDITOR
375: * 'v' means to use VISUAL
376: */
377: rewind(collf);
378: mesedit(collf, c);
379: goto cont;
380: }
381: }
382: goto out;
383: err:
384: if (collf != NULL) {
385: Fclose(collf);
386: collf = NULL;
387: }
388: out:
389: if (collf != NULL)
390: rewind(collf);
391: noreset--;
1.2 ! deraadt 392: sigemptyset(&nset);
! 393: sigaddset(&nset, SIGINT);
! 394: sigaddset(&nset, SIGHUP);
! 395: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 396: signal(SIGINT, saveint);
397: signal(SIGHUP, savehup);
398: signal(SIGTSTP, savetstp);
399: signal(SIGTTOU, savettou);
400: signal(SIGTTIN, savettin);
1.2 ! deraadt 401: sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 deraadt 402: return collf;
403: }
404:
405: /*
406: * Write a file, ex-like if f set.
407: */
408: int
409: exwrite(name, fp, f)
410: char name[];
411: FILE *fp;
412: int f;
413: {
414: register FILE *of;
415: register int c;
416: long cc;
417: int lc;
418: struct stat junk;
419:
420: if (f) {
421: printf("\"%s\" ", name);
422: fflush(stdout);
423: }
424: if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
425: if (!f)
426: fprintf(stderr, "%s: ", name);
427: fprintf(stderr, "File exists\n");
428: return(-1);
429: }
430: if ((of = Fopen(name, "w")) == NULL) {
431: perror(NOSTR);
432: return(-1);
433: }
434: lc = 0;
435: cc = 0;
436: while ((c = getc(fp)) != EOF) {
437: cc++;
438: if (c == '\n')
439: lc++;
440: (void) putc(c, of);
441: if (ferror(of)) {
442: perror(name);
443: Fclose(of);
444: return(-1);
445: }
446: }
447: Fclose(of);
448: printf("%d/%ld\n", lc, cc);
449: fflush(stdout);
450: return(0);
451: }
452:
453: /*
454: * Edit the message being collected on fp.
455: * On return, make the edit file the new temp file.
456: */
457: void
458: mesedit(fp, c)
459: FILE *fp;
460: int c;
461: {
462: sig_t sigint = signal(SIGINT, SIG_IGN);
463: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
464:
465: if (nf != NULL) {
466: fseek(nf, 0L, 2);
467: collf = nf;
468: Fclose(fp);
469: }
470: (void) signal(SIGINT, sigint);
471: }
472:
473: /*
474: * Pipe the message through the command.
475: * Old message is on stdin of command;
476: * New message collected from stdout.
477: * Sh -c must return 0 to accept the new message.
478: */
479: void
480: mespipe(fp, cmd)
481: FILE *fp;
482: char cmd[];
483: {
484: FILE *nf;
485: sig_t sigint = signal(SIGINT, SIG_IGN);
486: extern char *tempEdit;
487: char *shell;
488:
489: if ((nf = Fopen(tempEdit, "w+")) == NULL) {
490: perror(tempEdit);
491: goto out;
492: }
493: (void) unlink(tempEdit);
494: /*
495: * stdin = current message.
496: * stdout = new message.
497: */
498: if ((shell = value("SHELL")) == NOSTR)
499: shell = _PATH_CSHELL;
500: if (run_command(shell,
501: 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) {
502: (void) Fclose(nf);
503: goto out;
504: }
505: if (fsize(nf) == 0) {
506: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
507: (void) Fclose(nf);
508: goto out;
509: }
510: /*
511: * Take new files.
512: */
513: (void) fseek(nf, 0L, 2);
514: collf = nf;
515: (void) Fclose(fp);
516: out:
517: (void) signal(SIGINT, sigint);
518: }
519:
520: /*
521: * Interpolate the named messages into the current
522: * message, preceding each line with a tab.
523: * Return a count of the number of characters now in
524: * the message, or -1 if an error is encountered writing
525: * the message temporary. The flag argument is 'm' if we
526: * should shift over and 'f' if not.
527: */
528: int
529: forward(ms, fp, f)
530: char ms[];
531: FILE *fp;
532: int f;
533: {
534: register int *msgvec;
535: extern char *tempMail;
536: struct ignoretab *ig;
537: char *tabst;
538:
539: msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
540: if (msgvec == (int *) NOSTR)
541: return(0);
542: if (getmsglist(ms, msgvec, 0) < 0)
543: return(0);
544: if (*msgvec == 0) {
545: *msgvec = first(0, MMNORM);
546: if (*msgvec == NULL) {
547: printf("No appropriate messages\n");
548: return(0);
549: }
550: msgvec[1] = NULL;
551: }
552: if (f == 'f' || f == 'F')
553: tabst = NOSTR;
554: else if ((tabst = value("indentprefix")) == NOSTR)
555: tabst = "\t";
556: ig = isupper(f) ? NULL : ignore;
557: printf("Interpolating:");
558: for (; *msgvec != 0; msgvec++) {
559: struct message *mp = message + *msgvec - 1;
560:
561: touch(mp);
562: printf(" %d", *msgvec);
563: if (send(mp, fp, ig, tabst) < 0) {
564: perror(tempMail);
565: return(-1);
566: }
567: }
568: printf("\n");
569: return(0);
570: }
571:
572: /*
573: * Print (continue) when continued after ^Z.
574: */
575: /*ARGSUSED*/
576: void
577: collstop(s)
578: int s;
579: {
580: sig_t old_action = signal(s, SIG_DFL);
1.2 ! deraadt 581: sigset_t nset;
1.1 deraadt 582:
1.2 ! deraadt 583: sigemptyset(&nset);
! 584: sigaddset(&nset, s);
! 585: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 586: kill(0, s);
1.2 ! deraadt 587: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 588: signal(s, old_action);
589: if (colljmp_p) {
590: colljmp_p = 0;
591: hadintr = 0;
592: longjmp(colljmp, 1);
593: }
594: }
595:
596: /*
597: * On interrupt, come here to save the partial message in ~/dead.letter.
598: * Then jump out of the collection loop.
599: */
600: /*ARGSUSED*/
601: void
602: collint(s)
603: int s;
604: {
605: /*
606: * the control flow is subtle, because we can be called from ~q.
607: */
608: if (!hadintr) {
609: if (value("ignore") != NOSTR) {
610: puts("@");
611: fflush(stdout);
612: clearerr(stdin);
613: return;
614: }
615: hadintr = 1;
616: longjmp(colljmp, 1);
617: }
618: rewind(collf);
619: if (value("nosave") == NOSTR)
620: savedeadletter(collf);
621: longjmp(collabort, 1);
622: }
623:
624: /*ARGSUSED*/
625: void
626: collhup(s)
627: int s;
628: {
629: rewind(collf);
630: savedeadletter(collf);
631: /*
632: * Let's pretend nobody else wants to clean up,
633: * a true statement at this time.
634: */
635: exit(1);
636: }
637:
638: void
639: savedeadletter(fp)
640: register FILE *fp;
641: {
642: register FILE *dbuf;
643: register int c;
644: char *cp;
645:
646: if (fsize(fp) == 0)
647: return;
648: cp = getdeadletter();
649: c = umask(077);
650: dbuf = Fopen(cp, "a");
651: (void) umask(c);
652: if (dbuf == NULL)
653: return;
654: while ((c = getc(fp)) != EOF)
655: (void) putc(c, dbuf);
656: Fclose(dbuf);
657: rewind(fp);
658: }