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