1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 /* 40 * mailx -- a modified version of a University of California at Berkeley 41 * mail program 42 * 43 * Collect input from standard input, handling 44 * ~ escapes. 45 */ 46 47 #include "rcv.h" 48 #include <locale.h> 49 50 #ifdef SIGCONT 51 static void collcont(int); 52 #endif 53 static void collrub(int s); 54 static void cpout(char *str, FILE *ofd); 55 static int exwrite(char name[], FILE *ibuf); 56 static int forward(char ms[], FILE *obuf, int f); 57 static void intack(int); 58 static int forward(char ms[], FILE *obuf, int f); 59 static FILE *mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp); 60 static FILE *mespipe(FILE *ibuf, FILE *obuf, char cmd[]); 61 static void resetsigs(int resethup); 62 static int stripnulls(register char *linebuf, register int nread); 63 static void xhalt(void); 64 static char **Xaddone(char **hf, char news[]); 65 static int tabputs(const char *line, FILE *obuf); 66 67 /* 68 * Read a message from standard output and return a read file to it 69 * or NULL on error. 70 */ 71 72 /* 73 * The following hokiness with global variables is so that on 74 * receipt of an interrupt signal, the partial message can be salted 75 * away on dead.letter. The output file must be available to flush, 76 * and the input to read. Several open files could be saved all through 77 * mailx if stdio allowed simultaneous read/write access. 78 */ 79 80 static void (*savesig)(int); /* Previous SIGINT value */ 81 static void (*savehup)(int); /* Previous SIGHUP value */ 82 #ifdef SIGCONT 83 static void (*savecont)(int); /* Previous SIGCONT value */ 84 #endif 85 static FILE *newi; /* File for saving away */ 86 static FILE *newo; /* Output side of same */ 87 static int ignintr; /* Ignore interrups */ 88 static int hadintr; /* Have seen one SIGINT so far */ 89 static struct header *savehp; 90 static jmp_buf coljmp; /* To get back to work */ 91 92 FILE * 93 collect(struct header *hp) 94 { 95 FILE *ibuf, *fbuf, *obuf; 96 int escape, eof; 97 long lc, cc; 98 register int c, t; 99 int hdrs; 100 char linebuf[LINESIZE+1], *cp; 101 char *iprompt; 102 int inhead; 103 void (*sigpipe)(int), (*sigint)(int); 104 int fd = -1; 105 106 noreset++; 107 ibuf = obuf = NULL; 108 newi = newo = NULL; 109 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 110 (obuf = fdopen(fd, "w")) == NULL) { 111 perror(tempMail); 112 goto err; 113 } 114 newo = obuf; 115 if ((ibuf = fopen(tempMail, "r")) == NULL) { 116 perror(tempMail); 117 newo = NULL; 118 fclose(obuf); 119 goto err; 120 } 121 newi = ibuf; 122 removefile(tempMail); 123 124 ignintr = (int)value("ignore"); 125 hadintr = 1; 126 inhead = 1; 127 savehp = hp; 128 # ifdef VMUNIX 129 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) 130 sigset(SIGINT, ignintr ? intack : collrub), sigblock(sigmask(SIGINT)); 131 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) 132 sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP)); 133 # else /* VMUNIX */ 134 # ifdef OLD_BSD_SIGS 135 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) 136 sigset(SIGINT, ignintr ? intack : collrub); 137 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) 138 sigset(SIGHUP, collrub); 139 # else 140 if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN) { 141 sigset_t mask; 142 143 sigemptyset(&mask); 144 sigaddset(&mask, SIGINT); 145 sigset(SIGINT, ignintr ? intack : collrub); 146 sigprocmask(SIG_BLOCK, &mask, NULL); 147 } 148 if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN) { 149 sigset_t mask; 150 151 sigemptyset(&mask); 152 sigaddset(&mask, SIGHUP); 153 sigset(SIGHUP, collrub); 154 sigprocmask(SIG_BLOCK, &mask, NULL); 155 } 156 # endif 157 # endif /* VMUNIX */ 158 #ifdef SIGCONT 159 savecont = sigset(SIGCONT, collcont); 160 #endif 161 /* 162 * If we are going to prompt for subject/cc/bcc, 163 * refrain from printing a newline after 164 * the headers (since some people mind). 165 */ 166 167 if (hp->h_subject == NOSTR) { 168 hp->h_subject = sflag; 169 sflag = NOSTR; 170 } 171 if (hp->h_cc == NOSTR) { 172 hp->h_cc = cflag; 173 cflag = NOSTR; 174 } 175 if (hp->h_bcc == NOSTR) { 176 hp->h_bcc = bflag; 177 bflag = NOSTR; 178 } 179 t = GMASK; 180 hdrs = 0; 181 if (intty && !tflag) { 182 if (hp->h_to == NOSTR) 183 hdrs |= GTO; 184 if (hp->h_subject == NOSTR && value("asksub")) 185 hdrs |= GSUBJECT; 186 if (hp->h_cc == NOSTR && value("askcc")) 187 hdrs |= GCC; 188 if (hp->h_bcc == NOSTR && value("askbcc")) 189 hdrs |= GBCC; 190 if (hdrs) 191 t &= ~GNL; 192 } 193 if (hp->h_seq != 0) { 194 puthead(hp, stdout, t, 0); 195 fflush(stdout); 196 } 197 if (setjmp(coljmp)) 198 goto err; 199 escape = SENDESC; 200 if ((cp = value("escape")) != NOSTR) 201 escape = *cp; 202 eof = 0; 203 if ((cp = value("MAILX_HEAD")) != NOSTR) { 204 cpout( cp, obuf); 205 if (isatty(fileno(stdin))) 206 cpout( cp, stdout); 207 } 208 iprompt = value("iprompt"); 209 fflush(obuf); 210 hadintr = 0; 211 for (;;) { 212 int nread, hasnulls; 213 # ifdef VMUNIX 214 int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP)); 215 # else 216 # ifndef OLD_BSD_SIGS 217 sigset_t omask; 218 sigprocmask(0, NULL, &omask); 219 sigdelset(&omask, SIGINT); 220 sigdelset(&omask, SIGHUP); 221 # endif 222 # endif 223 224 setjmp(coljmp); 225 # ifdef VMUNIX 226 sigsetmask(omask); 227 # else /* VMUNIX */ 228 # ifdef OLD_BSD_SIGS 229 sigrelse(SIGINT); 230 sigrelse(SIGHUP); 231 # else 232 sigprocmask(SIG_SETMASK, &omask, NULL); 233 # endif 234 # endif /* VMUNIX */ 235 if (intty && !tflag && outtty && iprompt) 236 fputs(iprompt, stdout); 237 flush(); 238 if (hdrs) { 239 grabh(hp, hdrs, 1); 240 hdrs = 0; 241 continue; 242 } 243 if ((nread = getaline(linebuf,LINESIZE,stdin,&hasnulls)) == NULL) { 244 if (intty && value("ignoreeof") != NOSTR) { 245 if (++eof > 35) 246 break; 247 printf(gettext( 248 "Use \".\" to terminate letter\n")); 249 continue; 250 } 251 break; 252 } 253 eof = 0; 254 hadintr = 0; 255 if (intty && equal(".\n", linebuf) && 256 (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) 257 break; 258 /* 259 * If -t, scan text for headers. 260 */ 261 if (tflag) { 262 char *cp2; 263 264 if (!inhead) { 265 writeit: 266 if (write(fileno(obuf),linebuf,nread) != nread) 267 goto werr; 268 continue; 269 } 270 if (linebuf[0] == '\n') { 271 /* got blank line after header, ignore it */ 272 inhead = 0; 273 continue; 274 } 275 if (!headerp(linebuf)) { 276 /* got non-header line, save it */ 277 inhead = 0; 278 goto writeit; 279 } 280 if (hasnulls) 281 nread = stripnulls(linebuf, nread); 282 for (;;) { 283 char line2[LINESIZE]; 284 285 c = getc(stdin); 286 ungetc(c, stdin); 287 if (!isspace(c) || c == '\n') 288 break; 289 if (readline(stdin, line2) < 0) 290 break; 291 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); 292 cp2++) 293 ; 294 if (strlen(linebuf) + strlen(cp2) >= 295 (unsigned)LINESIZE-2) 296 break; 297 cp = &linebuf[strlen(linebuf)]; 298 while (cp > linebuf && 299 (isspace(cp[-1]) || cp[-1] == '\\')) 300 cp--; 301 *cp++ = ' '; 302 strcpy(cp, cp2); 303 } 304 if ((c = strlen(linebuf)) > 0) { 305 cp = &linebuf[c-1]; 306 while (cp > linebuf && isspace(*cp)) 307 cp--; 308 *++cp = 0; 309 } 310 if (ishfield(linebuf, "to")) 311 hp->h_to = addto(hp->h_to, hcontents(linebuf)); 312 else if (ishfield(linebuf, "subject")) 313 hp->h_subject = 314 addone(hp->h_subject, hcontents(linebuf)); 315 else if (ishfield(linebuf, "cc")) 316 hp->h_cc = addto(hp->h_cc, hcontents(linebuf)); 317 else if (ishfield(linebuf, "bcc")) 318 hp->h_bcc = 319 addto(hp->h_bcc, hcontents(linebuf)); 320 else if (ishfield(linebuf, "default-options")) 321 hp->h_defopt = 322 addone(hp->h_defopt, hcontents(linebuf)); 323 else 324 hp->h_others = Xaddone(hp->h_others, linebuf); 325 hp->h_seq++; 326 continue; 327 } 328 if ((linebuf[0] != escape) || (rflag != NOSTR) || 329 (!intty && !(int)value("escapeok"))) { 330 if (write(fileno(obuf),linebuf,nread) != nread) 331 goto werr; 332 continue; 333 } 334 /* 335 * On double escape, just send the single one. 336 */ 337 if ((nread > 1) && (linebuf[1] == escape)) { 338 if (write(fileno(obuf),linebuf+1,nread-1) != (nread-1)) 339 goto werr; 340 continue; 341 } 342 if (hasnulls) 343 nread = stripnulls(linebuf, nread); 344 c = linebuf[1]; 345 linebuf[nread - 1] = '\0'; 346 switch (c) { 347 default: 348 /* 349 * Otherwise, it's an error. 350 */ 351 printf(gettext("Unknown tilde escape.\n")); 352 break; 353 354 case 'a': 355 case 'A': 356 /* 357 * autograph; sign the letter. 358 */ 359 360 if (cp = value(c=='a' ? "sign":"Sign")) { 361 if (*cp) 362 cpout( cp, obuf); 363 if (isatty(fileno(stdin))) { 364 if (*cp) 365 cpout( cp, stdout); 366 } 367 } 368 369 break; 370 371 case 'i': 372 /* 373 * insert string 374 */ 375 for (cp = &linebuf[2]; any(*cp, " \t"); cp++) 376 ; 377 if (*cp) 378 cp = value(cp); 379 if (cp != NOSTR) { 380 if (*cp) 381 cpout(cp, obuf); 382 if (isatty(fileno(stdout))) { 383 if (*cp) 384 cpout(cp, stdout); 385 } 386 } 387 break; 388 389 case '!': 390 /* 391 * Shell escape, send the balance of the 392 * line to sh -c. 393 */ 394 395 shell(&linebuf[2]); 396 break; 397 398 case ':': 399 case '_': 400 /* 401 * Escape to command mode, but be nice! 402 */ 403 404 execute(&linebuf[2], 1); 405 iprompt = value("iprompt"); 406 if (cp = value("escape")) 407 escape = *cp; 408 printf(gettext("(continue)\n")); 409 break; 410 411 case '.': 412 /* 413 * Simulate end of file on input. 414 */ 415 goto eofl; 416 417 case 'q': 418 case 'Q': 419 /* 420 * Force a quit of sending mail. 421 * Act like an interrupt happened. 422 */ 423 424 hadintr++; 425 collrub(SIGINT); 426 exit(1); 427 /* NOTREACHED */ 428 429 case 'x': 430 xhalt(); 431 break; /* not reached */ 432 433 case 'h': 434 /* 435 * Grab a bunch of headers. 436 */ 437 if (!intty || !outtty) { 438 printf(gettext("~h: no can do!?\n")); 439 break; 440 } 441 grabh(hp, GMASK, (int)value("bsdcompat")); 442 printf(gettext("(continue)\n")); 443 break; 444 445 case 't': 446 /* 447 * Add to the To list. 448 */ 449 450 hp->h_to = addto(hp->h_to, &linebuf[2]); 451 hp->h_seq++; 452 break; 453 454 case 's': 455 /* 456 * Set the Subject list. 457 */ 458 459 cp = &linebuf[2]; 460 while (any(*cp, " \t")) 461 cp++; 462 hp->h_subject = savestr(cp); 463 hp->h_seq++; 464 break; 465 466 case 'c': 467 /* 468 * Add to the CC list. 469 */ 470 471 hp->h_cc = addto(hp->h_cc, &linebuf[2]); 472 hp->h_seq++; 473 break; 474 475 case 'b': 476 /* 477 * Add stuff to blind carbon copies list. 478 */ 479 hp->h_bcc = addto(hp->h_bcc, &linebuf[2]); 480 hp->h_seq++; 481 break; 482 483 case 'R': 484 hp->h_defopt = addone(hp->h_defopt, myname); 485 hp->h_seq++; 486 fprintf(stderr, gettext("Return receipt marked.\n")); 487 receipt_flg = 1; 488 break; 489 490 case 'd': 491 copy(Getf("DEAD"), &linebuf[2]); 492 /* FALLTHROUGH */ 493 494 case '<': 495 case 'r': { 496 int ispip; 497 /* 498 * Invoke a file: 499 * Search for the file name, 500 * then open it and copy the contents to obuf. 501 * 502 * if name begins with '!', read from a command 503 */ 504 505 cp = &linebuf[2]; 506 while (any(*cp, " \t")) 507 cp++; 508 if (*cp == '\0') { 509 printf(gettext("Interpolate what file?\n")); 510 break; 511 } 512 if (*cp=='!') { 513 /* take input from a command */ 514 ispip = 1; 515 if ((fbuf = npopen(++cp, "r"))==NULL) { 516 perror(""); 517 break; 518 } 519 sigint = sigset(SIGINT, SIG_IGN); 520 } else { 521 ispip = 0; 522 cp = expand(cp); 523 if (cp == NOSTR) 524 break; 525 if (isdir(cp)) { 526 printf(gettext("%s: directory\n"), cp); 527 break; 528 } 529 if ((fbuf = fopen(cp, "r")) == NULL) { 530 perror(cp); 531 break; 532 } 533 } 534 printf("\"%s\" ", cp); 535 flush(); 536 lc = cc = 0; 537 while ((t = getc(fbuf)) != EOF) { 538 if (t == '\n') 539 lc++; 540 if (putc(t, obuf) == EOF) { 541 if (ispip) { 542 npclose(fbuf); 543 sigset(SIGINT, sigint); 544 } else 545 fclose(fbuf); 546 goto werr; 547 } 548 cc++; 549 } 550 if (ispip) { 551 npclose(fbuf); 552 sigset(SIGINT, sigint); 553 } else 554 fclose(fbuf); 555 printf("%ld/%ld\n", lc, cc); 556 fflush(obuf); 557 break; 558 } 559 560 case 'w': 561 /* 562 * Write the message on a file. 563 */ 564 565 cp = &linebuf[2]; 566 while (any(*cp, " \t")) 567 cp++; 568 if (*cp == '\0') { 569 fprintf(stderr, gettext("Write what file!?\n")); 570 break; 571 } 572 if ((cp = expand(cp)) == NOSTR) 573 break; 574 fflush(obuf); 575 rewind(ibuf); 576 exwrite(cp, ibuf); 577 break; 578 579 case 'm': 580 case 'M': 581 case 'f': 582 case 'F': 583 /* 584 * Interpolate the named messages, if we 585 * are in receiving mail mode. Does the 586 * standard list processing garbage. 587 * If ~f or ~F is given, we don't shift over. 588 */ 589 590 if (!rcvmode) { 591 printf(gettext( 592 "No messages to send from!?!\n")); 593 break; 594 } 595 cp = &linebuf[2]; 596 while (any(*cp, " \t")) 597 cp++; 598 if (forward(cp, obuf, c) < 0) 599 goto werr; 600 fflush(obuf); 601 printf(gettext("(continue)\n")); 602 break; 603 604 case '?': 605 if ((fbuf = fopen(THELPFILE, "r")) == NULL) { 606 printf(gettext("No help just now.\n")); 607 break; 608 } 609 t = getc(fbuf); 610 while (t != -1) { 611 putchar(t); 612 t = getc(fbuf); 613 } 614 fclose(fbuf); 615 break; 616 617 case 'p': { 618 /* 619 * Print out the current state of the 620 * message without altering anything. 621 */ 622 int nlines; 623 extern jmp_buf pipestop; 624 extern void brokpipe(int); 625 626 fflush(obuf); 627 rewind(ibuf); 628 fbuf = stdout; 629 if (setjmp(pipestop)) 630 goto ret0; 631 if (intty && outtty && (cp = value("crt")) != NOSTR) { 632 nlines = 633 (*cp == '\0' ? screensize() : atoi(cp)) - 7; 634 /* 7 for hdr lines */ 635 while ((t = getc(ibuf)) != EOF) { 636 if (t == '\n') 637 if (--nlines <= 0) 638 break; 639 } 640 rewind(ibuf); 641 if (nlines <= 0) { 642 fbuf = npopen(MORE, "w"); 643 if (fbuf == NULL) { 644 perror(MORE); 645 fbuf = stdout; 646 } else { 647 sigint = sigset(SIGINT, SIG_IGN); 648 sigpipe = sigset(SIGPIPE, brokpipe); 649 } 650 } 651 } 652 fprintf(fbuf, gettext("-------\nMessage contains:\n")); 653 puthead(hp, fbuf, GMASK, 0); 654 while ((t = getc(ibuf))!=EOF) 655 putc(t, fbuf); 656 ret0: 657 if (fbuf != stdout) { 658 npclose(fbuf); 659 sigset(SIGPIPE, sigpipe); 660 sigset(SIGINT, sigint); 661 } 662 printf(gettext("(continue)\n")); 663 break; 664 } 665 666 case '^': 667 case '|': 668 /* 669 * Pipe message through command. 670 * Collect output as new message. 671 */ 672 673 obuf = mespipe(ibuf, obuf, &linebuf[2]); 674 newo = obuf; 675 ibuf = newi; 676 newi = ibuf; 677 printf(gettext("(continue)\n")); 678 break; 679 680 case 'v': 681 case 'e': 682 /* 683 * Edit the current message. 684 * 'e' means to use EDITOR 685 * 'v' means to use VISUAL 686 */ 687 688 if ((obuf = mesedit(ibuf, obuf, c, hp)) == NULL) 689 goto err; 690 newo = obuf; 691 ibuf = newi; 692 printf(gettext("(continue)\n")); 693 break; 694 } 695 fflush(obuf); 696 } 697 eofl: 698 fflush(obuf); 699 if ((cp = value("MAILX_TAIL")) != NOSTR) { 700 cpout( cp, obuf); 701 if (isatty(fileno(stdin))) 702 cpout( cp, stdout); 703 } 704 fclose(obuf); 705 rewind(ibuf); 706 resetsigs(0); 707 noreset = 0; 708 return(ibuf); 709 710 werr: 711 /* 712 * Write error occurred on tmp file, save partial 713 * message in dead.letter. 714 */ 715 perror(tempMail); 716 fflush(obuf); 717 rewind(ibuf); 718 if (fsize(ibuf) > 0) { 719 char *deadletter; 720 721 deadletter = Getf("DEAD"); 722 fprintf(stderr, gettext("Saving partial message in %s\n"), 723 deadletter); 724 if ((fbuf = fopen(deadletter, 725 value("appenddeadletter") == NOSTR ? "w" : "a")) != NULL) { 726 chmod(deadletter, DEADPERM); 727 puthead(hp, fbuf, GMASK|GCLEN, fsize(ibuf)); 728 lcwrite(deadletter, ibuf, fbuf, value("appenddeadletter") != NOSTR); 729 fclose(fbuf); 730 } else 731 perror(deadletter); 732 } 733 734 err: 735 if (ibuf != NULL) 736 fclose(ibuf); 737 if (obuf != NULL) 738 fclose(obuf); 739 resetsigs(0); 740 noreset = 0; 741 return(NULL); 742 } 743 744 static void 745 resetsigs(int resethup) 746 { 747 (void) sigset(SIGINT, savesig); 748 if (resethup) 749 (void) sigset(SIGHUP, savehup); 750 #ifdef SIGCONT 751 # ifdef preSVr4 752 (void) sigset(SIGCONT, savecont); 753 # else 754 { 755 struct sigaction nsig; 756 nsig.sa_handler = (void (*)())savecont; 757 sigemptyset(&nsig.sa_mask); 758 nsig.sa_flags = SA_RESTART; 759 (void) sigaction(SIGCONT, &nsig, (struct sigaction*)0); 760 } 761 # endif 762 #endif 763 } 764 765 /* 766 * Write a file ex-like. 767 */ 768 769 static int 770 exwrite(char name[], FILE *ibuf) 771 { 772 register FILE *of; 773 struct stat junk; 774 void (*sigint)(int), (*sigpipe)(int); 775 int pi = (*name == '!'); 776 777 if ((of = pi ? npopen(++name, "w") : fopen(name, "a")) == NULL) { 778 perror(name); 779 return(-1); 780 } 781 if (pi) { 782 sigint = sigset(SIGINT, SIG_IGN); 783 sigpipe = sigset(SIGPIPE, SIG_IGN); 784 } 785 lcwrite(name, ibuf, of, 0); 786 pi ? npclose(of) : fclose(of); 787 if (pi) { 788 sigset(SIGPIPE, sigpipe); 789 sigset(SIGINT, sigint); 790 } 791 return(0); 792 } 793 794 void 795 lcwrite(char *fn, FILE *fi, FILE *fo, int addnl) 796 { 797 register int c; 798 long lc, cc; 799 800 printf("\"%s\" ", fn); 801 fflush(stdout); 802 lc = cc = 0; 803 while ((c = getc(fi)) != EOF) { 804 cc++; 805 if (putc(c, fo) == '\n') 806 lc++; 807 if (ferror(fo)) { 808 perror(""); 809 return; 810 } 811 } 812 if (addnl) { 813 putc('\n', fo); 814 lc++; 815 cc++; 816 } 817 fflush(fo); 818 if (fferror(fo)) { 819 perror(""); 820 return; 821 } 822 printf("%ld/%ld\n", lc, cc); 823 fflush(stdout); 824 } 825 826 /* 827 * Edit the message being collected on ibuf and obuf. 828 * Write the message out onto some poorly-named temp file 829 * and point an editor at it. 830 * 831 * On return, make the edit file the new temp file. 832 */ 833 834 static FILE * 835 mesedit(FILE *ibuf, FILE *obuf, int c, struct header *hp) 836 { 837 pid_t pid; 838 FILE *fbuf; 839 register int t; 840 void (*sigint)(int); 841 #ifdef SIGCONT 842 void (*sigcont)(int); 843 #endif 844 struct stat sbuf; 845 register char *edit; 846 char hdr[LINESIZE]; 847 char *oto, *osubject, *occ, *obcc, **oothers; 848 int fd = -1; 849 850 if (stat(tempEdit, &sbuf) >= 0) { 851 printf(gettext("%s: file exists\n"), tempEdit); 852 goto out; 853 } 854 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 855 (fbuf = fdopen(fd, "w")) == NULL) { 856 perror(tempEdit); 857 goto out; 858 } 859 fflush(obuf); 860 rewind(ibuf); 861 puthead(hp, fbuf, GMASK, 0); 862 while ((t = getc(ibuf)) != EOF) 863 putc(t, fbuf); 864 fflush(fbuf); 865 if (fferror(fbuf)) { 866 perror(tempEdit); 867 removefile(tempEdit); 868 goto out; 869 } 870 fclose(fbuf); 871 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR || 872 *edit == '\0') 873 edit = c == 'e' ? EDITOR : VISUAL; 874 edit = safeexpand(edit); 875 876 /* 877 * Fork/execlp the editor on the edit file 878 */ 879 880 pid = vfork(); 881 if (pid == (pid_t)-1) { 882 perror("fork"); 883 removefile(tempEdit); 884 goto out; 885 } 886 if (pid == 0) { 887 char ecmd[BUFSIZ]; 888 char *Shell; 889 890 sigchild(); 891 execlp(edit, edit, tempEdit, (char *)0); 892 /* 893 * If execlp fails, "edit" might really be a complete 894 * shell command, not a simple pathname. Try using 895 * the shell to run it. 896 */ 897 snprintf(ecmd, sizeof (ecmd), "exec %s %s", edit, tempEdit); 898 if ((Shell = value("SHELL")) == NULL || *Shell=='\0') 899 Shell = SHELL; 900 execlp(Shell, Shell, "-c", ecmd, NULL); 901 perror(edit); 902 _exit(1); 903 } 904 sigint = sigset(SIGINT, SIG_IGN); 905 #ifdef SIGCONT 906 sigcont = sigset(SIGCONT, SIG_DFL); 907 #endif 908 while (wait((int *)0) != pid) 909 ; 910 sigset(SIGINT, sigint); 911 #ifdef SIGCONT 912 sigset(SIGCONT, sigcont); 913 #endif 914 /* 915 * Now switch to new file. 916 */ 917 918 if ((fbuf = fopen(tempEdit, "r")) == NULL) { 919 perror(tempEdit); 920 removefile(tempEdit); 921 goto out; 922 } 923 removefile(tempEdit); 924 925 /* save the old headers, in case they are accidentally deleted */ 926 osubject = hp->h_subject; 927 oto = hp->h_to; 928 occ = hp->h_cc; 929 obcc = hp->h_bcc; 930 oothers = hp->h_others; 931 hp->h_to = hp->h_subject = hp->h_cc = hp->h_bcc = hp->h_defopt = NOSTR; 932 hp->h_others = NOSTRPTR; 933 hp->h_seq = 0; 934 while (gethfield(fbuf, hdr, 9999L) > 0) { 935 if (ishfield(hdr, "to")) 936 hp->h_to = addto(hp->h_to, hcontents(hdr)); 937 else if (ishfield(hdr, "subject")) 938 hp->h_subject = addone(hp->h_subject, hcontents(hdr)); 939 else if (ishfield(hdr, "cc")) 940 hp->h_cc = addto(hp->h_cc, hcontents(hdr)); 941 else if (ishfield(hdr, "bcc")) 942 hp->h_bcc = addto(hp->h_bcc, hcontents(hdr)); 943 else if (ishfield(hdr, "default-options")) 944 hp->h_defopt = addone(hp->h_defopt, hcontents(hdr)); 945 else 946 hp->h_others = Xaddone(hp->h_others, hdr); 947 hp->h_seq++; 948 } 949 if (hp->h_seq == 0) { 950 /* if we didn't see any headers, restore the original headers */ 951 hp->h_subject = osubject; 952 hp->h_to = oto; 953 hp->h_cc = occ; 954 hp->h_bcc = obcc; 955 hp->h_others = oothers; 956 printf(gettext( 957 "(Deleted headers restored to original values)\n")); 958 } 959 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 960 (obuf = fdopen(fd, "w")) == NULL) { 961 perror(tempMail); 962 fclose(fbuf); 963 goto out; 964 } 965 if ((ibuf = fopen(tempMail, "r")) == NULL) { 966 perror(tempMail); 967 removefile(tempMail); 968 fclose(fbuf); 969 fclose(obuf); 970 goto out; 971 } 972 removefile(tempMail); 973 if (strlen(hdr) != 0) { 974 fputs(hdr, obuf); 975 putc('\n', obuf); 976 } 977 while ((t = getc(fbuf)) != EOF) 978 putc(t, obuf); 979 fclose(fbuf); 980 fclose(newo); 981 fclose(newi); 982 newo = obuf; 983 newi = ibuf; 984 out: 985 return(newo); 986 } 987 988 /* 989 * Pipe the message through the command. 990 * Old message is on stdin of command; 991 * New message collected from stdout. 992 * Sh -c must return 0 to accept the new message. 993 */ 994 995 static FILE * 996 mespipe(FILE *ibuf, FILE *obuf, char cmd[]) 997 { 998 register FILE *ni, *no; 999 pid_t pid; 1000 int s; 1001 void (*sigint)(int); 1002 char *Shell; 1003 int fd = -1; 1004 1005 newi = ibuf; 1006 if ((fd = open(tempEdit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 1007 (no = fdopen(fd, "w")) == NULL) { 1008 perror(tempEdit); 1009 return(obuf); 1010 } 1011 if ((ni = fopen(tempEdit, "r")) == NULL) { 1012 perror(tempEdit); 1013 fclose(no); 1014 removefile(tempEdit); 1015 return(obuf); 1016 } 1017 removefile(tempEdit); 1018 fflush(obuf); 1019 rewind(ibuf); 1020 if ((Shell = value("SHELL")) == NULL || *Shell=='\0') 1021 Shell = SHELL; 1022 if ((pid = vfork()) == (pid_t)-1) { 1023 perror("fork"); 1024 goto err; 1025 } 1026 if (pid == 0) { 1027 /* 1028 * stdin = current message. 1029 * stdout = new message. 1030 */ 1031 1032 sigchild(); 1033 close(0); 1034 dup(fileno(ibuf)); 1035 close(1); 1036 dup(fileno(no)); 1037 for (s = 4; s < 15; s++) 1038 close(s); 1039 execlp(Shell, Shell, "-c", cmd, (char *)0); 1040 perror(Shell); 1041 _exit(1); 1042 } 1043 sigint = sigset(SIGINT, SIG_IGN); 1044 while (wait(&s) != pid) 1045 ; 1046 sigset(SIGINT, sigint); 1047 if (s != 0 || pid == (pid_t)-1) { 1048 fprintf(stderr, gettext("\"%s\" failed!?\n"), cmd); 1049 goto err; 1050 } 1051 if (fsize(ni) == 0) { 1052 fprintf(stderr, gettext("No bytes from \"%s\" !?\n"), cmd); 1053 goto err; 1054 } 1055 1056 /* 1057 * Take new files. 1058 */ 1059 1060 newi = ni; 1061 fclose(ibuf); 1062 fclose(obuf); 1063 return(no); 1064 1065 err: 1066 fclose(no); 1067 fclose(ni); 1068 return(obuf); 1069 } 1070 1071 static char *indentprefix; /* used instead of tab by tabputs */ 1072 1073 /* 1074 * Interpolate the named messages into the current 1075 * message, preceding each line with a tab. 1076 * Return a count of the number of characters now in 1077 * the message, or -1 if an error is encountered writing 1078 * the message temporary. The flag argument is 'm' if we 1079 * should shift over and 'f' if not. 1080 */ 1081 static int 1082 forward(char ms[], FILE *obuf, int f) 1083 { 1084 register int *msgvec, *ip; 1085 1086 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 1087 if (msgvec == NOINTPTR) 1088 return(0); 1089 if (getmsglist(ms, msgvec, 0) < 0) 1090 return(0); 1091 if (*msgvec == NULL) { 1092 *msgvec = first(0, MMNORM); 1093 if (*msgvec == NULL) { 1094 printf(gettext("No appropriate messages\n")); 1095 return(0); 1096 } 1097 msgvec[1] = NULL; 1098 } 1099 if (tolower(f) == 'm') 1100 indentprefix = value("indentprefix"); 1101 printf(gettext("Interpolating:")); 1102 for (ip = msgvec; *ip != NULL; ip++) { 1103 touch(*ip); 1104 printf(" %d", *ip); 1105 if (msend(&message[*ip-1], obuf, islower(f) ? M_IGNORE : 0, 1106 tolower(f) == 'm' ? tabputs : fputs) < 0) { 1107 perror(tempMail); 1108 return(-1); 1109 } 1110 } 1111 fflush(obuf); 1112 if (fferror(obuf)) { 1113 perror(tempMail); 1114 return(-1); 1115 } 1116 printf("\n"); 1117 return(0); 1118 } 1119 1120 static int 1121 tabputs(const char *line, FILE *obuf) 1122 { 1123 1124 if (indentprefix) 1125 fputs(indentprefix, obuf); 1126 /* Don't create lines with only a tab on them */ 1127 else if (line[0] != '\n') 1128 fputc('\t', obuf); 1129 return (fputs(line, obuf)); 1130 } 1131 1132 /* 1133 * Print (continue) when continued after ^Z. 1134 */ 1135 #ifdef SIGCONT 1136 static void 1137 #ifdef __cplusplus 1138 collcont(int) 1139 #else 1140 /* ARGSUSED */ 1141 collcont(int s) 1142 #endif 1143 { 1144 printf(gettext("(continue)\n")); 1145 fflush(stdout); 1146 } 1147 #endif /* SIGCONT */ 1148 1149 /* 1150 * On interrupt, go here to save the partial 1151 * message on ~/dead.letter. 1152 * Then restore signals and execute the normal 1153 * signal routine. We only come here if signals 1154 * were previously set anyway. 1155 */ 1156 static void 1157 collrub(int s) 1158 { 1159 register FILE *dbuf; 1160 register char *deadletter; 1161 1162 # ifdef OLD_BSD_SIGS 1163 if (s == SIGHUP) 1164 sigignore(SIGHUP); 1165 # endif 1166 if (s == SIGINT && hadintr == 0) { 1167 hadintr++; 1168 fflush(stdout); 1169 fprintf(stderr, 1170 gettext("\n(Interrupt -- one more to kill letter)\n")); 1171 # ifdef OLD_BSD_SIGS 1172 sigrelse(s); 1173 # endif 1174 longjmp(coljmp, 1); 1175 } 1176 fclose(newo); 1177 rewind(newi); 1178 if (s == SIGINT && value("save")==NOSTR || fsize(newi) == 0) 1179 goto done; 1180 deadletter = Getf("DEAD"); 1181 if ((dbuf = fopen(deadletter, 1182 (value("appenddeadletter") == NOSTR ? "w" : "a"))) == NULL) { 1183 perror(deadletter); 1184 goto done; 1185 } 1186 chmod(deadletter, DEADPERM); 1187 puthead(savehp, dbuf, GMASK|GCLEN, fsize(newi)); 1188 lcwrite(deadletter, newi, dbuf, value("appenddeadletter") != NOSTR); 1189 fclose(dbuf); 1190 done: 1191 fclose(newi); 1192 resetsigs(1); 1193 if (rcvmode) { 1194 if (s == SIGHUP) 1195 hangup(s); 1196 else 1197 stop(s); 1198 } 1199 else 1200 exit(1); 1201 } 1202 1203 /* 1204 * Acknowledge an interrupt signal from the tty by typing an @ 1205 */ 1206 static void 1207 #ifdef __cplusplus 1208 intack(int) 1209 #else 1210 /* ARGSUSED */ 1211 intack(int s) 1212 #endif 1213 { 1214 1215 puts("@"); 1216 fflush(stdout); 1217 clearerr(stdin); 1218 longjmp(coljmp,1); 1219 } 1220 1221 /* Read line from stdin, noting any NULL characters. 1222 Return the number of characters read. Note that the buffer 1223 passed must be 1 larger than "size" for the trailing NUL byte. 1224 */ 1225 int 1226 getaline(char *line, int size, FILE *f, int *hasnulls) 1227 { 1228 register int i, ch; 1229 for (i = 0; (i < size) && ((ch=getc(f)) != EOF); ) { 1230 if ( ch == '\0' ) 1231 *hasnulls = 1; 1232 if ((line[i++] = (char)ch) == '\n') break; 1233 } 1234 line[i] = '\0'; 1235 return(i); 1236 } 1237 1238 void 1239 #ifdef __cplusplus 1240 savedead(int) 1241 #else 1242 /* ARGSUSED */ 1243 savedead(int s) 1244 #endif 1245 { 1246 collrub(SIGINT); 1247 exit(1); 1248 /* NOTREACHED */ 1249 } 1250 1251 /* 1252 * Add a list of addresses to the end of a header entry field. 1253 */ 1254 char * 1255 addto(char hf[], char news[]) 1256 { 1257 char name[LINESIZE]; 1258 int comma = docomma(news); 1259 1260 while (news = yankword(news, name, sizeof (name), comma)) { 1261 nstrcat(name, sizeof (name), ", "); 1262 hf = addone(hf, name); 1263 } 1264 return hf; 1265 } 1266 1267 /* 1268 * Add a string to the end of a header entry field. 1269 */ 1270 char * 1271 addone(char hf[], char news[]) 1272 { 1273 register char *cp, *cp2, *linebuf; 1274 1275 if (hf == NOSTR) 1276 hf = savestr(""); 1277 if (*news == '\0') 1278 return(hf); 1279 linebuf = (char *)srealloc(hf, (unsigned)(strlen(hf) + strlen(news) + 2)); 1280 cp2 = strchr(linebuf, '\0'); 1281 if (cp2 > linebuf && cp2[-1] != ' ') 1282 *cp2++ = ' '; 1283 for (cp = news; any(*cp, " \t"); cp++) 1284 ; 1285 while (*cp != '\0') 1286 *cp2++ = *cp++; 1287 *cp2 = '\0'; 1288 return(linebuf); 1289 } 1290 1291 static int 1292 nptrs(char **hf) 1293 { 1294 register int i; 1295 1296 if (!hf) 1297 return(0); 1298 for (i = 0; *hf; hf++) 1299 i++; 1300 return(i); 1301 } 1302 1303 /* 1304 * Add a non-standard header to the end of the non-standard headers. 1305 */ 1306 static char ** 1307 Xaddone(char **hf, char news[]) 1308 { 1309 register char *linebuf; 1310 char **ohf = hf; 1311 int nhf = nptrs(hf); 1312 1313 if (hf == NOSTRPTR) 1314 hf = (char**)salloc(sizeof(char*) * 2); 1315 else 1316 hf = (char**)srealloc(hf, sizeof(char*) * (nhf + 2)); 1317 if (hf == NOSTRPTR) { 1318 fprintf(stderr, gettext("No room, header lost: %s\n"), news); 1319 return(ohf); 1320 } 1321 linebuf = (char *)salloc((unsigned)(strlen(news) + 1)); 1322 strcpy(linebuf, news); 1323 hf[nhf++] = linebuf; 1324 hf[nhf] = NOSTR; 1325 return(hf); 1326 } 1327 1328 static void 1329 cpout(char *str, FILE *ofd) 1330 { 1331 register char *cp = str; 1332 1333 while (*cp) { 1334 if (*cp == '\\') { 1335 switch (*(cp+1)) { 1336 case 'n': 1337 putc('\n', ofd); 1338 cp++; 1339 break; 1340 case 't': 1341 putc('\t', ofd); 1342 cp++; 1343 break; 1344 default: 1345 putc('\\', ofd); 1346 } 1347 } else { 1348 putc(*cp, ofd); 1349 } 1350 cp++; 1351 } 1352 putc('\n', ofd); 1353 fflush(ofd); 1354 } 1355 1356 static void 1357 xhalt(void) 1358 { 1359 fclose(newo); 1360 fclose(newi); 1361 sigset(SIGINT, savesig); 1362 sigset(SIGHUP, savehup); 1363 if (rcvmode) 1364 stop(0); 1365 exit(1); 1366 /* NOTREACHED */ 1367 } 1368 1369 /* 1370 * Strip the nulls from a buffer of length n 1371 */ 1372 static int 1373 stripnulls(register char *linebuf, register int nread) 1374 { 1375 register int i, j; 1376 1377 for (i = 0; i < nread; i++) 1378 if (linebuf[i] == '\0') 1379 break; 1380 for (j = i; j < nread; j++) 1381 if (linebuf[j] != '\0') 1382 linebuf[i++] = linebuf[j]; 1383 linebuf[i] = '\0'; 1384 return(i); 1385 } 1386