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