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