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