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