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