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