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