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