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