Annotation of src/usr.bin/mail/collect.c, Revision 1.9
1.9 ! millert 1: /* $OpenBSD: collect.c,v 1.8 1997/07/14 00:24:25 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.9 ! millert 41: static char rcsid[] = "$OpenBSD: collect.c,v 1.8 1997/07/14 00:24:25 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.1 deraadt 155: if (getsub)
156: grabh(hp, GSUBJECT);
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: }
396: goto out;
397: err:
398: if (collf != NULL) {
1.6 millert 399: (void)Fclose(collf);
1.1 deraadt 400: collf = NULL;
401: }
402: out:
403: if (collf != NULL)
404: rewind(collf);
405: noreset--;
1.9 ! millert 406: sigemptyset(&nset);
! 407: sigaddset(&nset, SIGINT);
! 408: sigaddset(&nset, SIGHUP);
! 409: sigprocmask(SIG_BLOCK, &nset, &oset);
1.1 deraadt 410: signal(SIGINT, saveint);
411: signal(SIGHUP, savehup);
412: signal(SIGTSTP, savetstp);
413: signal(SIGTTOU, savettou);
414: signal(SIGTTIN, savettin);
1.9 ! millert 415: sigprocmask(SIG_SETMASK, &oset, NULL);
1.6 millert 416: return(collf);
1.1 deraadt 417: }
418:
419: /*
420: * Write a file, ex-like if f set.
421: */
422: int
423: exwrite(name, fp, f)
424: char name[];
425: FILE *fp;
426: int f;
427: {
428: register FILE *of;
429: register int c;
430: long cc;
431: int lc;
432: struct stat junk;
433:
434: if (f) {
435: printf("\"%s\" ", name);
436: fflush(stdout);
437: }
438: if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
439: if (!f)
440: fprintf(stderr, "%s: ", name);
1.6 millert 441: fputs("File exists\n", stderr);
1.1 deraadt 442: return(-1);
443: }
444: if ((of = Fopen(name, "w")) == NULL) {
1.8 millert 445: warn(NULL);
1.1 deraadt 446: return(-1);
447: }
448: lc = 0;
449: cc = 0;
450: while ((c = getc(fp)) != EOF) {
451: cc++;
452: if (c == '\n')
453: lc++;
1.7 millert 454: (void)putc(c, of);
1.1 deraadt 455: if (ferror(of)) {
1.6 millert 456: warn(name);
457: (void)Fclose(of);
1.1 deraadt 458: return(-1);
459: }
460: }
1.6 millert 461: (void)Fclose(of);
1.1 deraadt 462: printf("%d/%ld\n", lc, cc);
463: fflush(stdout);
464: return(0);
465: }
466:
467: /*
468: * Edit the message being collected on fp.
469: * On return, make the edit file the new temp file.
470: */
471: void
472: mesedit(fp, c)
473: FILE *fp;
474: int c;
475: {
476: sig_t sigint = signal(SIGINT, SIG_IGN);
477: FILE *nf = run_editor(fp, (off_t)-1, c, 0);
478:
479: if (nf != NULL) {
480: fseek(nf, 0L, 2);
481: collf = nf;
1.6 millert 482: (void)Fclose(fp);
1.1 deraadt 483: }
1.7 millert 484: (void)signal(SIGINT, sigint);
1.1 deraadt 485: }
486:
487: /*
488: * Pipe the message through the command.
489: * Old message is on stdin of command;
490: * New message collected from stdout.
491: * Sh -c must return 0 to accept the new message.
492: */
493: void
494: mespipe(fp, cmd)
495: FILE *fp;
496: char cmd[];
497: {
498: FILE *nf;
499: sig_t sigint = signal(SIGINT, SIG_IGN);
500: extern char *tempEdit;
501: char *shell;
502:
503: if ((nf = Fopen(tempEdit, "w+")) == NULL) {
1.6 millert 504: warn(tempEdit);
1.1 deraadt 505: goto out;
506: }
1.7 millert 507: (void)unlink(tempEdit);
1.1 deraadt 508: /*
509: * stdin = current message.
510: * stdout = new message.
511: */
1.8 millert 512: if ((shell = value("SHELL")) == NULL)
1.1 deraadt 513: shell = _PATH_CSHELL;
514: if (run_command(shell,
1.8 millert 515: 0, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) {
1.6 millert 516: (void)Fclose(nf);
1.1 deraadt 517: goto out;
518: }
519: if (fsize(nf) == 0) {
520: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
1.6 millert 521: (void)Fclose(nf);
1.1 deraadt 522: goto out;
523: }
524: /*
525: * Take new files.
526: */
1.7 millert 527: (void)fseek(nf, 0L, 2);
1.1 deraadt 528: collf = nf;
1.6 millert 529: (void)Fclose(fp);
1.1 deraadt 530: out:
1.7 millert 531: (void)signal(SIGINT, sigint);
1.1 deraadt 532: }
533:
534: /*
535: * Interpolate the named messages into the current
536: * message, preceding each line with a tab.
537: * Return a count of the number of characters now in
538: * the message, or -1 if an error is encountered writing
539: * the message temporary. The flag argument is 'm' if we
540: * should shift over and 'f' if not.
541: */
542: int
543: forward(ms, fp, f)
544: char ms[];
545: FILE *fp;
546: int f;
547: {
548: register int *msgvec;
549: extern char *tempMail;
550: struct ignoretab *ig;
551: char *tabst;
552:
1.7 millert 553: msgvec = (int *)salloc((msgCount+1) * sizeof(*msgvec));
1.8 millert 554: if (msgvec == NULL)
1.1 deraadt 555: return(0);
556: if (getmsglist(ms, msgvec, 0) < 0)
557: return(0);
558: if (*msgvec == 0) {
559: *msgvec = first(0, MMNORM);
560: if (*msgvec == NULL) {
1.6 millert 561: puts("No appropriate messages");
1.1 deraadt 562: return(0);
563: }
564: msgvec[1] = NULL;
565: }
566: if (f == 'f' || f == 'F')
1.8 millert 567: tabst = NULL;
568: else if ((tabst = value("indentprefix")) == NULL)
1.1 deraadt 569: tabst = "\t";
570: ig = isupper(f) ? NULL : ignore;
1.6 millert 571: fputs("Interpolating:", stdout);
1.1 deraadt 572: for (; *msgvec != 0; msgvec++) {
573: struct message *mp = message + *msgvec - 1;
574:
575: touch(mp);
576: printf(" %d", *msgvec);
577: if (send(mp, fp, ig, tabst) < 0) {
1.6 millert 578: warn(tempMail);
1.1 deraadt 579: return(-1);
580: }
581: }
1.6 millert 582: putchar('\n');
1.1 deraadt 583: return(0);
584: }
585:
586: /*
587: * Print (continue) when continued after ^Z.
588: */
589: /*ARGSUSED*/
590: void
591: collstop(s)
592: int s;
593: {
594: sig_t old_action = signal(s, SIG_DFL);
1.2 deraadt 595: sigset_t nset;
1.1 deraadt 596:
1.2 deraadt 597: sigemptyset(&nset);
598: sigaddset(&nset, s);
599: sigprocmask(SIG_UNBLOCK, &nset, NULL);
1.1 deraadt 600: kill(0, s);
1.2 deraadt 601: sigprocmask(SIG_BLOCK, &nset, NULL);
1.1 deraadt 602: signal(s, old_action);
603: if (colljmp_p) {
604: colljmp_p = 0;
605: hadintr = 0;
1.8 millert 606: siglongjmp(colljmp, 1);
1.1 deraadt 607: }
608: }
609:
610: /*
611: * On interrupt, come here to save the partial message in ~/dead.letter.
612: * Then jump out of the collection loop.
613: */
614: /*ARGSUSED*/
615: void
616: collint(s)
617: int s;
618: {
619: /*
620: * the control flow is subtle, because we can be called from ~q.
621: */
1.5 deraadt 622: if (hadintr == 0 && isatty(0)) {
1.8 millert 623: if (value("ignore") != NULL) {
1.1 deraadt 624: puts("@");
625: fflush(stdout);
626: clearerr(stdin);
627: return;
628: }
629: hadintr = 1;
1.8 millert 630: siglongjmp(colljmp, 1);
1.1 deraadt 631: }
632: rewind(collf);
1.8 millert 633: if (value("nosave") == NULL)
1.1 deraadt 634: savedeadletter(collf);
1.8 millert 635: siglongjmp(collabort, 1);
1.1 deraadt 636: }
637:
638: /*ARGSUSED*/
639: void
640: collhup(s)
641: int s;
642: {
643: rewind(collf);
644: savedeadletter(collf);
645: /*
646: * Let's pretend nobody else wants to clean up,
647: * a true statement at this time.
648: */
649: exit(1);
650: }
651:
652: void
653: savedeadletter(fp)
654: register FILE *fp;
655: {
656: register FILE *dbuf;
657: register int c;
658: char *cp;
659:
660: if (fsize(fp) == 0)
661: return;
662: cp = getdeadletter();
663: c = umask(077);
664: dbuf = Fopen(cp, "a");
1.7 millert 665: (void)umask(c);
1.1 deraadt 666: if (dbuf == NULL)
667: return;
668: while ((c = getc(fp)) != EOF)
1.7 millert 669: (void)putc(c, dbuf);
1.6 millert 670: (void)Fclose(dbuf);
1.1 deraadt 671: rewind(fp);
672: }