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