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