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