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.1 10/08/80"; 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 FILE *newi; /* File for saving away */ 30 static FILE *newo; /* Output side of same */ 31 static int hf; /* Ignore interrups */ 32 static int nofault; /* Soft signal if set */ 33 static int hadintr; /* Have seen one SIGINT so far */ 34 35 static jmp_buf coljmp; /* To get back to work */ 36 37 FILE * 38 collect(hp) 39 struct header *hp; 40 { 41 FILE *ibuf, *fbuf, *obuf; 42 int lc, cc, escape, collrub(), intack(), stopdot; 43 register int c, t; 44 char linebuf[LINESIZE], *cp; 45 extern char tempMail[]; 46 47 noreset++; 48 stopdot = (value("dot") != NOSTR) && intty; 49 ibuf = obuf = NULL; 50 if (value("ignore") != NOSTR) 51 hf = 1; 52 else 53 hf = 0; 54 nofault = 1; 55 hadintr = 0; 56 if ((savesig = signal(SIGINT, SIG_IGN)) != SIG_IGN) 57 signal(SIGINT, hf ? intack : collrub); 58 newi = NULL; 59 newo = NULL; 60 if ((obuf = fopen(tempMail, "w")) == NULL) { 61 perror(tempMail); 62 goto err; 63 } 64 newo = obuf; 65 if ((ibuf = fopen(tempMail, "r")) == NULL) { 66 perror(tempMail); 67 newo = NULL; 68 fclose(obuf); 69 goto err; 70 } 71 newi = ibuf; 72 remove(tempMail); 73 74 /* 75 * If we are going to prompt for a subject, 76 * refrain from printing a newline after 77 * the headers (since some people mind). 78 */ 79 80 t = GTO|GSUBJECT|GCC|GNL; 81 c = 0; 82 if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask")) 83 t &= ~GNL, c++; 84 if (hp->h_seq != 0) { 85 puthead(hp, stdout, t); 86 fflush(stdout); 87 } 88 if (c) 89 grabh(hp, GSUBJECT); 90 escape = ESCAPE; 91 if ((cp = value("escape")) != NOSTR) 92 escape = *cp; 93 for (;;) { 94 setjmp(coljmp); 95 nofault = 0; 96 flush(); 97 if (readline(stdin, linebuf) <= 0) 98 break; 99 hadintr = 0; 100 if (stopdot && equal(".", linebuf)) 101 break; 102 if (linebuf[0] != escape || 103 (!intty && value("henry") == NOSTR)) { 104 if ((t = putline(obuf, linebuf)) < 0) 105 goto err; 106 continue; 107 } 108 c = linebuf[1]; 109 nofault= 0; 110 switch (c) { 111 default: 112 /* 113 * On double escape, just send the single one. 114 * Otherwise, it's an error. 115 */ 116 117 if (c == escape) { 118 if (putline(obuf, &linebuf[1]) < 0) 119 goto err; 120 else 121 break; 122 } 123 printf("Unknown tilde escape.\n"); 124 break; 125 126 case 'C': 127 /* 128 * Dump core. 129 */ 130 131 core(); 132 break; 133 134 case '!': 135 /* 136 * Shell escape, send the balance of the 137 * line to sh -c. 138 */ 139 140 shell(&linebuf[2]); 141 break; 142 143 case ':': 144 case '_': 145 /* 146 * Escape to command mode, but be nice! 147 */ 148 149 nofault = 0; 150 execute(&linebuf[2]); 151 break; 152 153 case '.': 154 /* 155 * Simulate end of file on input. 156 */ 157 goto eof; 158 159 case 'q': 160 case 'Q': 161 /* 162 * Force a quit of sending mail. 163 * Act like an interrupt happened. 164 */ 165 166 nofault = 0; 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 nofault = 0; 308 if ((fbuf = fopen(THELPFILE, "r")) == NULL) { 309 printf("No help just now.\n"); 310 break; 311 } 312 t = getc(fbuf); 313 while (t != -1) { 314 putchar(t); 315 t = getc(fbuf); 316 } 317 fclose(fbuf); 318 break; 319 320 case 'p': 321 /* 322 * Print out the current state of the 323 * message without altering anything. 324 */ 325 326 fflush(obuf); 327 rewind(ibuf); 328 nofault = 0; 329 printf("-------\nMessage contains:\n"); 330 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 331 t = getc(ibuf); 332 while (t != EOF) { 333 putchar(t); 334 t = getc(ibuf); 335 } 336 printf("(continue)\n"); 337 break; 338 339 case '^': 340 case '|': 341 /* 342 * Pipe message through command. 343 * Collect output as new message. 344 */ 345 346 obuf = mespipe(ibuf, obuf, &linebuf[2]); 347 newo = obuf; 348 ibuf = newi; 349 newi = ibuf; 350 printf("(continue)\n"); 351 break; 352 353 case 'v': 354 case 'e': 355 /* 356 * Edit the current message. 357 * 'e' means to use EDITOR 358 * 'v' means to use VISUAL 359 */ 360 361 if ((obuf = mesedit(ibuf, obuf, c)) == NULL) 362 goto err; 363 newo = obuf; 364 ibuf = newi; 365 printf("(continue)\n"); 366 break; 367 break; 368 } 369 } 370 eof: 371 fclose(obuf); 372 rewind(ibuf); 373 signal(SIGINT, savesig); 374 noreset = 0; 375 return(ibuf); 376 377 err: 378 if (ibuf != NULL) 379 fclose(ibuf); 380 if (obuf != NULL) 381 fclose(obuf); 382 signal(SIGINT, savesig); 383 noreset = 0; 384 return(NULL); 385 } 386 387 /* 388 * Non destructively interrogate the value of the given signal. 389 */ 390 391 psig(n) 392 { 393 register (*wassig)(); 394 395 wassig = signal(n, SIG_IGN); 396 signal(n, wassig); 397 return((int) wassig); 398 } 399 400 /* 401 * Write a file, ex-like if f set. 402 */ 403 404 exwrite(name, ibuf, f) 405 char name[]; 406 FILE *ibuf; 407 { 408 register FILE *of; 409 register int c; 410 long cc; 411 int lc; 412 struct stat junk; 413 414 if (f) { 415 printf("\"%s\" ", name); 416 fflush(stdout); 417 } 418 if (stat(name, &junk) >= 0) { 419 if (!f) 420 fprintf(stderr, "%s: ", name); 421 fprintf(stderr, "File exists\n", name); 422 return(-1); 423 } 424 if ((of = fopen(name, "w")) == NULL) { 425 perror(NOSTR); 426 return(-1); 427 } 428 lc = 0; 429 cc = 0; 430 while ((c = getc(ibuf)) != EOF) { 431 cc++; 432 if (c == '\n') 433 lc++; 434 putc(c, of); 435 if (ferror(of)) { 436 perror(name); 437 fclose(of); 438 return(-1); 439 } 440 } 441 fclose(of); 442 printf("%d/%ld\n", lc, cc); 443 fflush(stdout); 444 return(0); 445 } 446 447 /* 448 * Edit the message being collected on ibuf and obuf. 449 * Write the message out onto some poorly-named temp file 450 * and point an editor at it. 451 * 452 * On return, make the edit file the new temp file. 453 */ 454 455 FILE * 456 mesedit(ibuf, obuf, c) 457 FILE *ibuf, *obuf; 458 { 459 int pid, s; 460 FILE *fbuf; 461 register int t; 462 int (*sig)(); 463 struct stat sbuf; 464 extern char tempMail[], tempEdit[]; 465 register char *edit; 466 467 sig = signal(SIGINT, SIG_IGN); 468 if (stat(tempEdit, &sbuf) >= 0) { 469 printf("%s: file exists\n", tempEdit); 470 goto out; 471 } 472 close(creat(tempEdit, 0600)); 473 if ((fbuf = fopen(tempEdit, "w")) == NULL) { 474 perror(tempEdit); 475 goto out; 476 } 477 fflush(obuf); 478 rewind(ibuf); 479 t = getc(ibuf); 480 while (t != EOF) { 481 putc(t, fbuf); 482 t = getc(ibuf); 483 } 484 fflush(fbuf); 485 if (ferror(fbuf)) { 486 perror(tempEdit); 487 remove(tempEdit); 488 goto fix; 489 } 490 fclose(fbuf); 491 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR) 492 edit = c == 'e' ? EDITOR : VISUAL; 493 pid = vfork(); 494 if (pid == 0) { 495 if (sig != SIG_IGN) 496 signal(SIGINT, SIG_DFL); 497 execl(edit, edit, tempEdit, 0); 498 perror(edit); 499 _exit(1); 500 } 501 if (pid == -1) { 502 perror("fork"); 503 remove(tempEdit); 504 goto out; 505 } 506 while (wait(&s) != pid) 507 ; 508 if (s != 0) { 509 printf("Fatal error in \"%s\"\n", edit); 510 remove(tempEdit); 511 goto out; 512 } 513 514 /* 515 * Now switch to new file. 516 */ 517 518 if ((fbuf = fopen(tempEdit, "a")) == NULL) { 519 perror(tempEdit); 520 remove(tempEdit); 521 goto out; 522 } 523 if ((ibuf = fopen(tempEdit, "r")) == NULL) { 524 perror(tempEdit); 525 fclose(fbuf); 526 remove(tempEdit); 527 goto out; 528 } 529 remove(tempEdit); 530 fclose(obuf); 531 fclose(newi); 532 obuf = fbuf; 533 goto out; 534 fix: 535 perror(tempEdit); 536 out: 537 signal(SIGINT, sig); 538 newi = ibuf; 539 return(obuf); 540 } 541 542 /* 543 * Pipe the message through the command. 544 * Old message is on stdin of command; 545 * New message collected from stdout. 546 * Sh -c must return 0 to accept the new message. 547 */ 548 549 FILE * 550 mespipe(ibuf, obuf, cmd) 551 FILE *ibuf, *obuf; 552 char cmd[]; 553 { 554 register FILE *ni, *no; 555 int pid, s; 556 int (*savesig)(); 557 char *Shell; 558 559 newi = ibuf; 560 if ((no = fopen(tempEdit, "w")) == NULL) { 561 perror(tempEdit); 562 return(obuf); 563 } 564 if ((ni = fopen(tempEdit, "r")) == NULL) { 565 perror(tempEdit); 566 fclose(no); 567 remove(tempEdit); 568 return(obuf); 569 } 570 remove(tempEdit); 571 savesig = signal(SIGINT, SIG_IGN); 572 fflush(obuf); 573 rewind(ibuf); 574 if ((Shell = value("SHELL")) == NULL) 575 Shell = "/bin/sh"; 576 if ((pid = vfork()) == -1) { 577 perror("fork"); 578 goto err; 579 } 580 if (pid == 0) { 581 /* 582 * stdin = current message. 583 * stdout = new message. 584 */ 585 586 close(0); 587 dup(fileno(ibuf)); 588 close(1); 589 dup(fileno(no)); 590 for (s = 4; s < 15; s++) 591 close(s); 592 execl(Shell, Shell, "-c", cmd, 0); 593 perror(Shell); 594 _exit(1); 595 } 596 while (wait(&s) != pid) 597 ; 598 if (s != 0 || pid == -1) { 599 fprintf(stderr, "\"%s\" failed!?\n", cmd); 600 goto err; 601 } 602 if (fsize(ni) == 0) { 603 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 604 goto err; 605 } 606 607 /* 608 * Take new files. 609 */ 610 611 newi = ni; 612 fclose(ibuf); 613 fclose(obuf); 614 signal(SIGINT, savesig); 615 return(no); 616 617 err: 618 fclose(no); 619 fclose(ni); 620 signal(SIGINT, savesig); 621 return(obuf); 622 } 623 624 /* 625 * Interpolate the named messages into the current 626 * message, preceding each line with a tab. 627 * Return a count of the number of characters now in 628 * the message, or -1 if an error is encountered writing 629 * the message temporary. The flag argument is 'm' if we 630 * should shift over and 'f' if not. 631 */ 632 633 forward(ms, obuf, f) 634 char ms[]; 635 FILE *obuf; 636 { 637 register int *msgvec, *ip; 638 extern char tempMail[]; 639 640 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 641 if (msgvec == (int *) NOSTR) 642 return(0); 643 if (getmsglist(ms, msgvec, 0) < 0) 644 return(0); 645 if (*msgvec == NULL) { 646 *msgvec = first(0, MMNORM); 647 if (*msgvec == NULL) { 648 printf("No appropriate messages\n"); 649 return(0); 650 } 651 msgvec[1] = NULL; 652 } 653 printf("Interpolating:"); 654 for (ip = msgvec; *ip != NULL; ip++) { 655 touch(*ip); 656 printf(" %d", *ip); 657 if (f == 'm') { 658 if (transmit(&message[*ip-1], obuf) < 0) { 659 perror(tempMail); 660 return(-1); 661 } 662 } else 663 if (send(&message[*ip-1], obuf) < 0) { 664 perror(tempMail); 665 return(-1); 666 } 667 } 668 printf("\n"); 669 return(0); 670 } 671 672 /* 673 * Send message described by the passed pointer to the 674 * passed output buffer. Insert a tab in front of each 675 * line. Return a count of the characters sent, or -1 676 * on error. 677 */ 678 679 transmit(mailp, obuf) 680 struct message *mailp; 681 FILE *obuf; 682 { 683 register struct message *mp; 684 register int c, ch; 685 int n, bol; 686 FILE *ibuf; 687 688 mp = mailp; 689 ibuf = setinput(mp); 690 c = msize(mp); 691 n = c; 692 bol = 1; 693 while (c-- > 0) { 694 if (bol) { 695 bol = 0; 696 putc('\t', obuf); 697 n++; 698 if (ferror(obuf)) { 699 perror("/tmp"); 700 return(-1); 701 } 702 } 703 ch = getc(ibuf); 704 if (ch == '\n') 705 bol++; 706 putc(ch, obuf); 707 if (ferror(obuf)) { 708 perror("/tmp"); 709 return(-1); 710 } 711 } 712 return(n); 713 } 714 715 /* 716 * On interrupt, go here to save the partial 717 * message on #/dead.letter. 718 * Then restore signals and execute the normal 719 * signal routine. We only come here if signals 720 * were previously set anyway. 721 */ 722 723 collrub(s) 724 { 725 register FILE *dbuf; 726 register int c; 727 728 #ifdef V7 729 signal(s, SIG_IGN); 730 #else 731 signal(SIGINT, SIG_IGN); 732 #endif 733 if (nofault) { 734 #ifdef V7 735 signal(s, collrub); 736 #else 737 signal(SIGINT, collrub); 738 #endif 739 return; 740 } 741 if (hadintr == 0) { 742 hadintr++; 743 clrbuf(stdout); 744 printf("\n(Interrupt -- one more to kill letter)\n"); 745 #ifdef V7 746 signal(s, collrub); 747 #else 748 signal(SIGINT, collrub); 749 #endif 750 longjmp(coljmp, 1); 751 } 752 fclose(newo); 753 rewind(newi); 754 if (value("nosave") != NOSTR || fsize(newi) == 0) 755 goto done; 756 if ((dbuf = fopen(deadletter, "w")) == NULL) 757 goto done; 758 chmod(deadletter, 0600); 759 while ((c = getc(newi)) != EOF) 760 putc(c, dbuf); 761 fclose(dbuf); 762 763 done: 764 fclose(newi); 765 signal(SIGINT, savesig); 766 if (rcvmode) 767 stop(); 768 else 769 exit(1); 770 } 771 772 /* 773 * Acknowledge an interrupt signal from the tty by typing an @ 774 */ 775 776 intack(s) 777 { 778 779 signal(SIGINT, SIG_IGN); 780 puts("@"); 781 fflush(stdout); 782 clearerr(stdin); 783 signal(SIGINT, intack); 784 } 785 786 /* 787 * Add a string to the end of a header entry field. 788 */ 789 790 char * 791 addto(hf, news) 792 char hf[], news[]; 793 { 794 register char *cp, *cp2, *linebuf; 795 796 if (hf == NOSTR) 797 hf = ""; 798 if (*news == '\0') 799 return(hf); 800 linebuf = salloc(strlen(hf) + strlen(news) + 2); 801 for (cp = hf; any(*cp, " \t"); cp++) 802 ; 803 for (cp2 = linebuf; *cp;) 804 *cp2++ = *cp++; 805 *cp2++ = ' '; 806 for (cp = news; any(*cp, " \t"); cp++) 807 ; 808 while (*cp != '\0') 809 *cp2++ = *cp++; 810 *cp2 = '\0'; 811 return(linebuf); 812 } 813