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