|
|
1.1 root 1: #
2:
3: /*
4: * Mail -- a mail program
5: *
6: * Collect input from standard input, handling
7: * ~ escapes.
8: */
9:
10: #include "rcv.h"
11: #include <sys/stat.h>
12:
13: /*
14: * Read a message from standard output and return a read file to it
15: * or NULL on error.
16: */
17:
18: /*
19: * The following hokiness with global variables is so that on
20: * receipt of an interrupt signal, the partial message can be salted
21: * away on dead.letter. The output file must be available to flush,
22: * and the input to read. Several open files could be saved all through
23: * Mail if stdio allowed simultaneous read/write access.
24: */
25:
26: static int (*savesig)(); /* Previous SIGINT value */
27: static FILE *newi; /* File for saving away */
28: static FILE *newo; /* Output side of same */
29: static int hf; /* Ignore interrups */
30: static int nofault; /* Soft signal if set */
31: static int hadintr; /* Have seen one SIGINT so far */
32:
33: static jmp_buf coljmp; /* To get back to work */
34:
35: FILE *
36: collect(hp)
37: struct header *hp;
38: {
39: FILE *ibuf, *fbuf, *obuf;
40: int lc, cc, escape, collrub(), intack(), stopdot;
41: register int c, t;
42: char linebuf[LINESIZE], *cp;
43: extern char tempMail[];
44:
45: noreset++;
46: stopdot = (value("dot") != NOSTR) && intty;
47: ibuf = obuf = NULL;
48: if (value("ignore") != NOSTR)
49: hf = 1;
50: else
51: hf = 0;
52: nofault = 1;
53: hadintr = 0;
54: if ((savesig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
55: signal(SIGINT, hf ? intack : collrub);
56: newi = NULL;
57: newo = NULL;
58: if ((obuf = fopen(tempMail, "w")) == NULL) {
59: perror(tempMail);
60: goto err;
61: }
62: newo = obuf;
63: if ((ibuf = fopen(tempMail, "r")) == NULL) {
64: perror(tempMail);
65: newo = NULL;
66: fclose(obuf);
67: goto err;
68: }
69: newi = ibuf;
70: remove(tempMail);
71:
72: /*
73: * If we are going to prompt for a subject,
74: * refrain from printing a newline after
75: * the headers (since some people mind).
76: */
77:
78: t = GTO|GSUBJECT|GCC|GNL;
79: c = 0;
80: if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask"))
81: t &= ~GNL, c++;
82: if (hp->h_seq != 0) {
83: puthead(hp, stdout, t);
84: fflush(stdout);
85: }
86: if (c)
87: grabh(hp, GSUBJECT);
88: escape = ESCAPE;
89: if ((cp = value("escape")) != NOSTR)
90: escape = *cp;
91: for (;;) {
92: setjmp(coljmp);
93: nofault = 0;
94: flush();
95: if (readline(stdin, linebuf) <= 0)
96: break;
97: hadintr = 0;
98: if (stopdot && equal(".", linebuf))
99: break;
100: if (linebuf[0] != escape ||
101: (!intty && value("henry") == NOSTR)) {
102: if ((t = putline(obuf, linebuf)) < 0)
103: goto err;
104: continue;
105: }
106: c = linebuf[1];
107: nofault= 0;
108: switch (c) {
109: default:
110: /*
111: * On double escape, just send the single one.
112: * Otherwise, it's an error.
113: */
114:
115: if (c == escape) {
116: if (putline(obuf, &linebuf[1]) < 0)
117: goto err;
118: else
119: break;
120: }
121: printf("Unknown tilde escape.\n");
122: break;
123:
124: case 'C':
125: /*
126: * Dump core.
127: */
128:
129: core();
130: break;
131:
132: case '!':
133: /*
134: * Shell escape, send the balance of the
135: * line to sh -c.
136: */
137:
138: shell(&linebuf[2]);
139: break;
140:
141: case ':':
142: case '_':
143: /*
144: * Escape to command mode, but be nice!
145: */
146:
147: nofault = 0;
148: execute(&linebuf[2]);
149: break;
150:
151: case '.':
152: /*
153: * Simulate end of file on input.
154: */
155: goto eof;
156:
157: case 'q':
158: case 'Q':
159: /*
160: * Force a quit of sending mail.
161: * Act like an interrupt happened.
162: */
163:
164: nofault = 0;
165: hadintr++;
166: collrub(SIGINT);
167: exit(1);
168:
169: case 'h':
170: /*
171: * Grab a bunch of headers.
172: */
173: if (!intty || !outtty) {
174: printf("~h: no can do!?\n");
175: break;
176: }
177: grabh(hp, GTO|GSUBJECT|GCC|GBCC);
178: printf("(continue)\n");
179: break;
180:
181: case 't':
182: /*
183: * Add to the To list.
184: */
185:
186: hp->h_to = addto(hp->h_to, &linebuf[2]);
187: hp->h_seq++;
188: break;
189:
190: case 's':
191: /*
192: * Set the Subject list.
193: */
194:
195: cp = &linebuf[2];
196: while (any(*cp, " \t"))
197: cp++;
198: hp->h_subject = savestr(cp);
199: hp->h_seq++;
200: break;
201:
202: case 'c':
203: /*
204: * Add to the CC list.
205: */
206:
207: hp->h_cc = addto(hp->h_cc, &linebuf[2]);
208: hp->h_seq++;
209: break;
210:
211: case 'b':
212: /*
213: * Add stuff to blind carbon copies list.
214: */
215: hp->h_bcc = addto(hp->h_bcc, &linebuf[2]);
216: hp->h_seq++;
217: break;
218:
219: case 'd':
220: copy(deadletter, &linebuf[2]);
221: /* fall into . . . */
222:
223: case 'r':
224: /*
225: * Invoke a file:
226: * Search for the file name,
227: * then open it and copy the contents to obuf.
228: */
229:
230: cp = &linebuf[2];
231: while (any(*cp, " \t"))
232: cp++;
233: if (*cp == '\0') {
234: printf("Interpolate what file?\n");
235: break;
236: }
237: cp = expand(cp);
238: if (cp == NOSTR)
239: break;
240: if (isdir(cp)) {
241: printf("%s: directory\n");
242: break;
243: }
244: if ((fbuf = fopen(cp, "r")) == NULL) {
245: perror(cp);
246: break;
247: }
248: printf("\"%s\" ", cp);
249: flush();
250: lc = 0;
251: cc = 0;
252: while (readline(fbuf, linebuf) > 0) {
253: lc++;
254: if ((t = putline(obuf, linebuf)) < 0) {
255: fclose(fbuf);
256: goto err;
257: }
258: cc += t;
259: }
260: fclose(fbuf);
261: printf("%d/%d\n", lc, cc);
262: break;
263:
264: case 'w':
265: /*
266: * Write the message on a file.
267: */
268:
269: cp = &linebuf[2];
270: while (any(*cp, " \t"))
271: cp++;
272: if (*cp == '\0') {
273: fprintf(stderr, "Write what file!?\n");
274: break;
275: }
276: if ((cp = expand(cp)) == NOSTR)
277: break;
278: fflush(obuf);
279: rewind(ibuf);
280: exwrite(cp, ibuf, 1);
281: break;
282:
283: case 'm':
284: case 'f':
285: /*
286: * Interpolate the named messages, if we
287: * are in receiving mail mode. Does the
288: * standard list processing garbage.
289: * If ~f is given, we don't shift over.
290: */
291:
292: if (!rcvmode) {
293: printf("No messages to send from!?!\n");
294: break;
295: }
296: cp = &linebuf[2];
297: while (any(*cp, " \t"))
298: cp++;
299: if (forward(cp, obuf, c) < 0)
300: goto err;
301: printf("(continue)\n");
302: break;
303:
304: case '?':
305: nofault = 0;
306: if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
307: printf("No help just now.\n");
308: break;
309: }
310: t = getc(fbuf);
311: while (t != -1) {
312: putchar(t);
313: t = getc(fbuf);
314: }
315: fclose(fbuf);
316: break;
317:
318: case 'p':
319: /*
320: * Print out the current state of the
321: * message without altering anything.
322: */
323:
324: fflush(obuf);
325: rewind(ibuf);
326: nofault = 0;
327: printf("-------\nMessage contains:\n");
328: puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
329: t = getc(ibuf);
330: while (t != EOF) {
331: putchar(t);
332: t = getc(ibuf);
333: }
334: printf("(continue)\n");
335: break;
336:
337: case '^':
338: case '|':
339: /*
340: * Pipe message through command.
341: * Collect output as new message.
342: */
343:
344: obuf = mespipe(ibuf, obuf, &linebuf[2]);
345: newo = obuf;
346: ibuf = newi;
347: newi = ibuf;
348: printf("(continue)\n");
349: break;
350:
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:
359: if ((obuf = mesedit(ibuf, obuf, c)) == NULL)
360: goto err;
361: newo = obuf;
362: ibuf = newi;
363: printf("(continue)\n");
364: break;
365: break;
366: }
367: }
368: eof:
369: fclose(obuf);
370: rewind(ibuf);
371: signal(SIGINT, savesig);
372: noreset = 0;
373: return(ibuf);
374:
375: err:
376: if (ibuf != NULL)
377: fclose(ibuf);
378: if (obuf != NULL)
379: fclose(obuf);
380: signal(SIGINT, savesig);
381: noreset = 0;
382: return(NULL);
383: }
384:
385: /*
386: * Non destructively interrogate the value of the given signal.
387: */
388:
389: psig(n)
390: {
391: register (*wassig)();
392:
393: wassig = signal(n, SIG_IGN);
394: signal(n, wassig);
395: return((int) wassig);
396: }
397:
398: /*
399: * Write a file, ex-like if f set.
400: */
401:
402: exwrite(name, ibuf, f)
403: char name[];
404: FILE *ibuf;
405: {
406: register FILE *of;
407: register int c;
408: long cc;
409: int lc;
410: struct stat junk;
411:
412: if (f) {
413: printf("\"%s\" ", name);
414: fflush(stdout);
415: }
416: if (stat(name, &junk) >= 0) {
417: if (!f)
418: fprintf(stderr, "%s: ", name);
419: fprintf(stderr, "File exists\n", name);
420: return(-1);
421: }
422: if ((of = fopen(name, "w")) == NULL) {
423: perror(NOSTR);
424: return(-1);
425: }
426: lc = 0;
427: cc = 0;
428: while ((c = getc(ibuf)) != EOF) {
429: cc++;
430: if (c == '\n')
431: lc++;
432: putc(c, of);
433: if (ferror(of)) {
434: perror(name);
435: fclose(of);
436: return(-1);
437: }
438: }
439: fclose(of);
440: printf("%d/%ld\n", lc, cc);
441: fflush(stdout);
442: return(0);
443: }
444:
445: /*
446: * Edit the message being collected on ibuf and obuf.
447: * Write the message out onto some poorly-named temp file
448: * and point an editor at it.
449: *
450: * On return, make the edit file the new temp file.
451: */
452:
453: FILE *
454: mesedit(ibuf, obuf, c)
455: FILE *ibuf, *obuf;
456: {
457: int pid, s;
458: FILE *fbuf;
459: register int t;
460: int (*sig)();
461: struct stat sbuf;
462: extern char tempMail[], tempEdit[];
463: register char *edit;
464:
465: sig = signal(SIGINT, SIG_IGN);
466: if (stat(tempEdit, &sbuf) >= 0) {
467: printf("%s: file exists\n", tempEdit);
468: goto out;
469: }
470: close(creat(tempEdit, 0600));
471: if ((fbuf = fopen(tempEdit, "w")) == NULL) {
472: perror(tempEdit);
473: goto out;
474: }
475: fflush(obuf);
476: rewind(ibuf);
477: t = getc(ibuf);
478: while (t != EOF) {
479: putc(t, fbuf);
480: t = getc(ibuf);
481: }
482: fflush(fbuf);
483: if (ferror(fbuf)) {
484: perror(tempEdit);
485: remove(tempEdit);
486: goto fix;
487: }
488: fclose(fbuf);
489: if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
490: edit = c == 'e' ? EDITOR : VISUAL;
491: pid = vfork();
492: if (pid == 0) {
493: if (sig != SIG_IGN)
494: signal(SIGINT, SIG_DFL);
495: execl(edit, edit, tempEdit, 0);
496: perror(edit);
497: _exit(1);
498: }
499: if (pid == -1) {
500: perror("fork");
501: remove(tempEdit);
502: goto out;
503: }
504: while (wait(&s) != pid)
505: ;
506: if (s != 0) {
507: printf("Fatal error in \"%s\"\n", edit);
508: remove(tempEdit);
509: goto out;
510: }
511:
512: /*
513: * Now switch to new file.
514: */
515:
516: if ((fbuf = fopen(tempEdit, "a")) == NULL) {
517: perror(tempEdit);
518: remove(tempEdit);
519: goto out;
520: }
521: if ((ibuf = fopen(tempEdit, "r")) == NULL) {
522: perror(tempEdit);
523: fclose(fbuf);
524: remove(tempEdit);
525: goto out;
526: }
527: remove(tempEdit);
528: fclose(obuf);
529: fclose(newi);
530: obuf = fbuf;
531: goto out;
532: fix:
533: perror(tempEdit);
534: out:
535: signal(SIGINT, sig);
536: newi = ibuf;
537: return(obuf);
538: }
539:
540: /*
541: * Pipe the message through the command.
542: * Old message is on stdin of command;
543: * New message collected from stdout.
544: * Sh -c must return 0 to accept the new message.
545: */
546:
547: FILE *
548: mespipe(ibuf, obuf, cmd)
549: FILE *ibuf, *obuf;
550: char cmd[];
551: {
552: register FILE *ni, *no;
553: int pid, s;
554: int (*savesig)();
555: char *Shell;
556:
557: newi = ibuf;
558: if ((no = fopen(tempEdit, "w")) == NULL) {
559: perror(tempEdit);
560: return(obuf);
561: }
562: if ((ni = fopen(tempEdit, "r")) == NULL) {
563: perror(tempEdit);
564: fclose(no);
565: remove(tempEdit);
566: return(obuf);
567: }
568: remove(tempEdit);
569: savesig = signal(SIGINT, SIG_IGN);
570: fflush(obuf);
571: rewind(ibuf);
572: if ((Shell = value("SHELL")) == NULL)
573: Shell = "/bin/sh";
574: if ((pid = vfork()) == -1) {
575: perror("fork");
576: goto err;
577: }
578: if (pid == 0) {
579: /*
580: * stdin = current message.
581: * stdout = new message.
582: */
583:
584: close(0);
585: dup(fileno(ibuf));
586: close(1);
587: dup(fileno(no));
588: for (s = 4; s < 15; s++)
589: close(s);
590: execl(Shell, Shell, "-c", cmd, 0);
591: perror(Shell);
592: _exit(1);
593: }
594: while (wait(&s) != pid)
595: ;
596: if (s != 0 || pid == -1) {
597: fprintf(stderr, "\"%s\" failed!?\n", cmd);
598: goto err;
599: }
600: if (fsize(ni) == 0) {
601: fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
602: goto err;
603: }
604:
605: /*
606: * Take new files.
607: */
608:
609: newi = ni;
610: fclose(ibuf);
611: fclose(obuf);
612: signal(SIGINT, savesig);
613: return(no);
614:
615: err:
616: fclose(no);
617: fclose(ni);
618: signal(SIGINT, savesig);
619: return(obuf);
620: }
621:
622: /*
623: * Interpolate the named messages into the current
624: * message, preceding each line with a tab.
625: * Return a count of the number of characters now in
626: * the message, or -1 if an error is encountered writing
627: * the message temporary. The flag argument is 'm' if we
628: * should shift over and 'f' if not.
629: */
630:
631: forward(ms, obuf, f)
632: char ms[];
633: FILE *obuf;
634: {
635: register int *msgvec, *ip;
636: extern char tempMail[];
637:
638: msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
639: if (msgvec == (int *) NOSTR)
640: return(0);
641: if (getmsglist(ms, msgvec, 0) < 0)
642: return(0);
643: if (*msgvec == NULL) {
644: *msgvec = first(0, MMNORM);
645: if (*msgvec == NULL) {
646: printf("No appropriate messages\n");
647: return(0);
648: }
649: msgvec[1] = NULL;
650: }
651: printf("Interpolating:");
652: for (ip = msgvec; *ip != NULL; ip++) {
653: touch(*ip);
654: printf(" %d", *ip);
655: if (f == 'm') {
656: if (transmit(&message[*ip-1], obuf) < 0) {
657: perror(tempMail);
658: return(-1);
659: }
660: } else
661: if (send(&message[*ip-1], obuf) < 0) {
662: perror(tempMail);
663: return(-1);
664: }
665: }
666: printf("\n");
667: return(0);
668: }
669:
670: /*
671: * Send message described by the passed pointer to the
672: * passed output buffer. Insert a tab in front of each
673: * line. Return a count of the characters sent, or -1
674: * on error.
675: */
676:
677: transmit(mailp, obuf)
678: struct message *mailp;
679: FILE *obuf;
680: {
681: register struct message *mp;
682: register int c, ch;
683: int n, bol;
684: FILE *ibuf;
685:
686: mp = mailp;
687: ibuf = setinput(mp);
688: c = msize(mp);
689: n = c;
690: bol = 1;
691: while (c-- > 0) {
692: if (bol) {
693: bol = 0;
694: putc('\t', obuf);
695: n++;
696: if (ferror(obuf)) {
697: perror("/tmp");
698: return(-1);
699: }
700: }
701: ch = getc(ibuf);
702: if (ch == '\n')
703: bol++;
704: putc(ch, obuf);
705: if (ferror(obuf)) {
706: perror("/tmp");
707: return(-1);
708: }
709: }
710: return(n);
711: }
712:
713: /*
714: * On interrupt, go here to save the partial
715: * message on #/dead.letter.
716: * Then restore signals and execute the normal
717: * signal routine. We only come here if signals
718: * were previously set anyway.
719: */
720:
721: collrub(s)
722: {
723: register FILE *dbuf;
724: register int c;
725:
726: #ifdef V7
727: signal(s, SIG_IGN);
728: #else
729: signal(SIGINT, SIG_IGN);
730: #endif
731: if (nofault) {
732: #ifdef V7
733: signal(s, collrub);
734: #else
735: signal(SIGINT, collrub);
736: #endif
737: return;
738: }
739: if (hadintr == 0) {
740: hadintr++;
741: clrbuf(stdout);
742: printf("\n(Interrupt -- one more to kill letter)\n");
743: #ifdef V7
744: signal(s, collrub);
745: #else
746: signal(SIGINT, collrub);
747: #endif
748: longjmp(coljmp, 1);
749: }
750: fclose(newo);
751: rewind(newi);
752: if (value("nosave") != NOSTR || fsize(newi) == 0)
753: goto done;
754: if ((dbuf = fopen(deadletter, "w")) == NULL)
755: goto done;
756: chmod(deadletter, 0600);
757: while ((c = getc(newi)) != EOF)
758: putc(c, dbuf);
759: fclose(dbuf);
760:
761: done:
762: fclose(newi);
763: signal(SIGINT, savesig);
764: if (rcvmode)
765: stop();
766: else
767: exit(1);
768: }
769:
770: /*
771: * Acknowledge an interrupt signal from the tty by typing an @
772: */
773:
774: intack(s)
775: {
776:
777: signal(SIGINT, SIG_IGN);
778: puts("@");
779: fflush(stdout);
780: clearerr(stdin);
781: signal(SIGINT, intack);
782: }
783:
784: /*
785: * Add a string to the end of a header entry field.
786: */
787:
788: char *
789: addto(hf, news)
790: char hf[], news[];
791: {
792: register char *cp, *cp2, *linebuf;
793:
794: if (hf == NOSTR)
795: hf = "";
796: if (*news == '\0')
797: return(hf);
798: linebuf = salloc(strlen(hf) + strlen(news) + 2);
799: for (cp = hf; any(*cp, " \t"); cp++)
800: ;
801: for (cp2 = linebuf; *cp;)
802: *cp2++ = *cp++;
803: *cp2++ = ' ';
804: for (cp = news; any(*cp, " \t"); cp++)
805: ;
806: while (*cp != '\0')
807: *cp2++ = *cp++;
808: *cp2 = '\0';
809: return(linebuf);
810: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.