1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char sccsid[] = "from: @(#)collect.c 8.2 (Berkeley) 4/19/94"; 36 static char rcsid[] = "$Id: collect.c,v 1.5 1995/02/08 16:15:52 jtc Exp $"; 37 #endif /* not lint */ 38 39 /* 40 * Mail -- a mail program 41 * 42 * Collect input from standard input, handling 43 * ~ escapes. 44 */ 45 46 #include "rcv.h" 47 #include "extern.h" 48 49 /* 50 * Read a message from standard output and return a read file to it 51 * or NULL on error. 52 */ 53 54 /* 55 * The following hokiness with global variables is so that on 56 * receipt of an interrupt signal, the partial message can be salted 57 * away on dead.letter. 58 */ 59 60 static sig_t saveint; /* Previous SIGINT value */ 61 static sig_t savehup; /* Previous SIGHUP value */ 62 static sig_t savetstp; /* Previous SIGTSTP value */ 63 static sig_t savettou; /* Previous SIGTTOU value */ 64 static sig_t savettin; /* Previous SIGTTIN value */ 65 static FILE *collf; /* File for saving away */ 66 static int hadintr; /* Have seen one SIGINT so far */ 67 68 static jmp_buf colljmp; /* To get back to work */ 69 static int colljmp_p; /* whether to long jump */ 70 static jmp_buf collabort; /* To end collection with error */ 71 72 FILE * 73 collect(hp, printheaders) 74 struct header *hp; 75 int printheaders; 76 { 77 FILE *fbuf; 78 int lc, cc, escape, eofcount; 79 register int c, t; 80 char linebuf[LINESIZE], *cp; 81 extern char *tempMail; 82 char getsub; 83 int omask; 84 void collint(), collhup(), collstop(); 85 86 collf = NULL; 87 /* 88 * Start catching signals from here, but we're still die on interrupts 89 * until we're in the main loop. 90 */ 91 omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); 92 if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 93 signal(SIGINT, collint); 94 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) 95 signal(SIGHUP, collhup); 96 savetstp = signal(SIGTSTP, collstop); 97 savettou = signal(SIGTTOU, collstop); 98 savettin = signal(SIGTTIN, collstop); 99 if (setjmp(collabort) || setjmp(colljmp)) { 100 rm(tempMail); 101 goto err; 102 } 103 sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP))); 104 105 noreset++; 106 if ((collf = Fopen(tempMail, "w+")) == NULL) { 107 perror(tempMail); 108 goto err; 109 } 110 unlink(tempMail); 111 112 /* 113 * If we are going to prompt for a subject, 114 * refrain from printing a newline after 115 * the headers (since some people mind). 116 */ 117 t = GTO|GSUBJECT|GCC|GNL; 118 getsub = 0; 119 if (hp->h_subject == NOSTR && value("interactive") != NOSTR && 120 (value("ask") != NOSTR || value("asksub") != NOSTR)) 121 t &= ~GNL, getsub++; 122 if (printheaders) { 123 puthead(hp, stdout, t); 124 fflush(stdout); 125 } 126 if ((cp = value("escape")) != NOSTR) 127 escape = *cp; 128 else 129 escape = ESCAPE; 130 eofcount = 0; 131 hadintr = 0; 132 133 if (!setjmp(colljmp)) { 134 if (getsub) 135 grabh(hp, GSUBJECT); 136 } else { 137 /* 138 * Come here for printing the after-signal message. 139 * Duplicate messages won't be printed because 140 * the write is aborted if we get a SIGTTOU. 141 */ 142 cont: 143 if (hadintr) { 144 fflush(stdout); 145 fprintf(stderr, 146 "\n(Interrupt -- one more to kill letter)\n"); 147 } else { 148 printf("(continue)\n"); 149 fflush(stdout); 150 } 151 } 152 for (;;) { 153 colljmp_p = 1; 154 c = readline(stdin, linebuf, LINESIZE); 155 colljmp_p = 0; 156 if (c < 0) { 157 if (value("interactive") != NOSTR && 158 value("ignoreeof") != NOSTR && ++eofcount < 25) { 159 printf("Use \".\" to terminate letter\n"); 160 continue; 161 } 162 break; 163 } 164 eofcount = 0; 165 hadintr = 0; 166 if (linebuf[0] == '.' && linebuf[1] == '\0' && 167 value("interactive") != NOSTR && 168 (value("dot") != NOSTR || value("ignoreeof") != NOSTR)) 169 break; 170 if (linebuf[0] != escape || value("interactive") == NOSTR) { 171 if (putline(collf, linebuf) < 0) 172 goto err; 173 continue; 174 } 175 c = linebuf[1]; 176 switch (c) { 177 default: 178 /* 179 * On double escape, just send the single one. 180 * Otherwise, it's an error. 181 */ 182 if (c == escape) { 183 if (putline(collf, &linebuf[1]) < 0) 184 goto err; 185 else 186 break; 187 } 188 printf("Unknown tilde escape.\n"); 189 break; 190 case 'C': 191 /* 192 * Dump core. 193 */ 194 core(); 195 break; 196 case '!': 197 /* 198 * Shell escape, send the balance of the 199 * line to sh -c. 200 */ 201 shell(&linebuf[2]); 202 break; 203 case ':': 204 case '_': 205 /* 206 * Escape to command mode, but be nice! 207 */ 208 execute(&linebuf[2], 1); 209 goto cont; 210 case '.': 211 /* 212 * Simulate end of file on input. 213 */ 214 goto out; 215 case 'q': 216 /* 217 * Force a quit of sending mail. 218 * Act like an interrupt happened. 219 */ 220 hadintr++; 221 collint(SIGINT); 222 exit(1); 223 case 'h': 224 /* 225 * Grab a bunch of headers. 226 */ 227 grabh(hp, GTO|GSUBJECT|GCC|GBCC); 228 goto cont; 229 case 't': 230 /* 231 * Add to the To list. 232 */ 233 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 234 break; 235 case 's': 236 /* 237 * Set the Subject list. 238 */ 239 cp = &linebuf[2]; 240 while (isspace(*cp)) 241 cp++; 242 hp->h_subject = savestr(cp); 243 break; 244 case 'c': 245 /* 246 * Add to the CC list. 247 */ 248 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 249 break; 250 case 'b': 251 /* 252 * Add stuff to blind carbon copies list. 253 */ 254 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 255 break; 256 case 'd': 257 strcpy(linebuf + 2, getdeadletter()); 258 /* fall into . . . */ 259 case 'r': 260 case '<': 261 /* 262 * Invoke a file: 263 * Search for the file name, 264 * then open it and copy the contents to collf. 265 */ 266 cp = &linebuf[2]; 267 while (isspace(*cp)) 268 cp++; 269 if (*cp == '\0') { 270 printf("Interpolate what file?\n"); 271 break; 272 } 273 cp = expand(cp); 274 if (cp == NOSTR) 275 break; 276 if (isdir(cp)) { 277 printf("%s: Directory\n", cp); 278 break; 279 } 280 if ((fbuf = Fopen(cp, "r")) == NULL) { 281 perror(cp); 282 break; 283 } 284 printf("\"%s\" ", cp); 285 fflush(stdout); 286 lc = 0; 287 cc = 0; 288 while (readline(fbuf, linebuf, LINESIZE) >= 0) { 289 lc++; 290 if ((t = putline(collf, linebuf)) < 0) { 291 Fclose(fbuf); 292 goto err; 293 } 294 cc += t; 295 } 296 Fclose(fbuf); 297 printf("%d/%d\n", lc, cc); 298 break; 299 case 'w': 300 /* 301 * Write the message on a file. 302 */ 303 cp = &linebuf[2]; 304 while (*cp == ' ' || *cp == '\t') 305 cp++; 306 if (*cp == '\0') { 307 fprintf(stderr, "Write what file!?\n"); 308 break; 309 } 310 if ((cp = expand(cp)) == NOSTR) 311 break; 312 rewind(collf); 313 exwrite(cp, collf, 1); 314 break; 315 case 'm': 316 case 'M': 317 case 'f': 318 case 'F': 319 /* 320 * Interpolate the named messages, if we 321 * are in receiving mail mode. Does the 322 * standard list processing garbage. 323 * If ~f is given, we don't shift over. 324 */ 325 if (forward(linebuf + 2, collf, c) < 0) 326 goto err; 327 goto cont; 328 case '?': 329 if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 330 perror(_PATH_TILDE); 331 break; 332 } 333 while ((t = getc(fbuf)) != EOF) 334 (void) putchar(t); 335 Fclose(fbuf); 336 break; 337 case 'p': 338 /* 339 * Print out the current state of the 340 * message without altering anything. 341 */ 342 rewind(collf); 343 printf("-------\nMessage contains:\n"); 344 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 345 while ((t = getc(collf)) != EOF) 346 (void) putchar(t); 347 goto cont; 348 case '|': 349 /* 350 * Pipe message through command. 351 * Collect output as new message. 352 */ 353 rewind(collf); 354 mespipe(collf, &linebuf[2]); 355 goto cont; 356 case 'v': 357 case 'e': 358 /* 359 * Edit the current message. 360 * 'e' means to use EDITOR 361 * 'v' means to use VISUAL 362 */ 363 rewind(collf); 364 mesedit(collf, c); 365 goto cont; 366 } 367 } 368 goto out; 369 err: 370 if (collf != NULL) { 371 Fclose(collf); 372 collf = NULL; 373 } 374 out: 375 if (collf != NULL) 376 rewind(collf); 377 noreset--; 378 sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); 379 signal(SIGINT, saveint); 380 signal(SIGHUP, savehup); 381 signal(SIGTSTP, savetstp); 382 signal(SIGTTOU, savettou); 383 signal(SIGTTIN, savettin); 384 sigsetmask(omask); 385 return collf; 386 } 387 388 /* 389 * Write a file, ex-like if f set. 390 */ 391 int 392 exwrite(name, fp, f) 393 char name[]; 394 FILE *fp; 395 int f; 396 { 397 register FILE *of; 398 register int c; 399 long cc; 400 int lc; 401 struct stat junk; 402 403 if (f) { 404 printf("\"%s\" ", name); 405 fflush(stdout); 406 } 407 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) { 408 if (!f) 409 fprintf(stderr, "%s: ", name); 410 fprintf(stderr, "File exists\n"); 411 return(-1); 412 } 413 if ((of = Fopen(name, "w")) == NULL) { 414 perror(NOSTR); 415 return(-1); 416 } 417 lc = 0; 418 cc = 0; 419 while ((c = getc(fp)) != EOF) { 420 cc++; 421 if (c == '\n') 422 lc++; 423 (void) putc(c, of); 424 if (ferror(of)) { 425 perror(name); 426 Fclose(of); 427 return(-1); 428 } 429 } 430 Fclose(of); 431 printf("%d/%ld\n", lc, cc); 432 fflush(stdout); 433 return(0); 434 } 435 436 /* 437 * Edit the message being collected on fp. 438 * On return, make the edit file the new temp file. 439 */ 440 void 441 mesedit(fp, c) 442 FILE *fp; 443 int c; 444 { 445 sig_t sigint = signal(SIGINT, SIG_IGN); 446 FILE *nf = run_editor(fp, (off_t)-1, c, 0); 447 448 if (nf != NULL) { 449 fseek(nf, 0L, 2); 450 collf = nf; 451 Fclose(fp); 452 } 453 (void) signal(SIGINT, sigint); 454 } 455 456 /* 457 * Pipe the message through the command. 458 * Old message is on stdin of command; 459 * New message collected from stdout. 460 * Sh -c must return 0 to accept the new message. 461 */ 462 void 463 mespipe(fp, cmd) 464 FILE *fp; 465 char cmd[]; 466 { 467 FILE *nf; 468 sig_t sigint = signal(SIGINT, SIG_IGN); 469 extern char *tempEdit; 470 char *shell; 471 472 if ((nf = Fopen(tempEdit, "w+")) == NULL) { 473 perror(tempEdit); 474 goto out; 475 } 476 (void) unlink(tempEdit); 477 /* 478 * stdin = current message. 479 * stdout = new message. 480 */ 481 if ((shell = value("SHELL")) == NOSTR) 482 shell = _PATH_CSHELL; 483 if (run_command(shell, 484 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) { 485 (void) Fclose(nf); 486 goto out; 487 } 488 if (fsize(nf) == 0) { 489 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 490 (void) Fclose(nf); 491 goto out; 492 } 493 /* 494 * Take new files. 495 */ 496 (void) fseek(nf, 0L, 2); 497 collf = nf; 498 (void) Fclose(fp); 499 out: 500 (void) signal(SIGINT, sigint); 501 } 502 503 /* 504 * Interpolate the named messages into the current 505 * message, preceding each line with a tab. 506 * Return a count of the number of characters now in 507 * the message, or -1 if an error is encountered writing 508 * the message temporary. The flag argument is 'm' if we 509 * should shift over and 'f' if not. 510 */ 511 int 512 forward(ms, fp, f) 513 char ms[]; 514 FILE *fp; 515 int f; 516 { 517 register int *msgvec; 518 extern char *tempMail; 519 struct ignoretab *ig; 520 char *tabst; 521 522 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 523 if (msgvec == (int *) NOSTR) 524 return(0); 525 if (getmsglist(ms, msgvec, 0) < 0) 526 return(0); 527 if (*msgvec == 0) { 528 *msgvec = first(0, MMNORM); 529 if (*msgvec == NULL) { 530 printf("No appropriate messages\n"); 531 return(0); 532 } 533 msgvec[1] = NULL; 534 } 535 if (f == 'f' || f == 'F') 536 tabst = NOSTR; 537 else if ((tabst = value("indentprefix")) == NOSTR) 538 tabst = "\t"; 539 ig = isupper(f) ? NULL : ignore; 540 printf("Interpolating:"); 541 for (; *msgvec != 0; msgvec++) { 542 struct message *mp = message + *msgvec - 1; 543 544 touch(mp); 545 printf(" %d", *msgvec); 546 if (send(mp, fp, ig, tabst) < 0) { 547 perror(tempMail); 548 return(-1); 549 } 550 } 551 printf("\n"); 552 return(0); 553 } 554 555 /* 556 * Print (continue) when continued after ^Z. 557 */ 558 /*ARGSUSED*/ 559 void 560 collstop(s) 561 int s; 562 { 563 sig_t old_action = signal(s, SIG_DFL); 564 565 sigsetmask(sigblock(0) & ~sigmask(s)); 566 kill(0, s); 567 sigblock(sigmask(s)); 568 signal(s, old_action); 569 if (colljmp_p) { 570 colljmp_p = 0; 571 hadintr = 0; 572 longjmp(colljmp, 1); 573 } 574 } 575 576 /* 577 * On interrupt, come here to save the partial message in ~/dead.letter. 578 * Then jump out of the collection loop. 579 */ 580 /*ARGSUSED*/ 581 void 582 collint(s) 583 int s; 584 { 585 /* 586 * the control flow is subtle, because we can be called from ~q. 587 */ 588 if (!hadintr) { 589 if (value("ignore") != NOSTR) { 590 puts("@"); 591 fflush(stdout); 592 clearerr(stdin); 593 return; 594 } 595 hadintr = 1; 596 longjmp(colljmp, 1); 597 } 598 rewind(collf); 599 if (value("nosave") == NOSTR) 600 savedeadletter(collf); 601 longjmp(collabort, 1); 602 } 603 604 /*ARGSUSED*/ 605 void 606 collhup(s) 607 int s; 608 { 609 rewind(collf); 610 savedeadletter(collf); 611 /* 612 * Let's pretend nobody else wants to clean up, 613 * a true statement at this time. 614 */ 615 exit(1); 616 } 617 618 void 619 savedeadletter(fp) 620 register FILE *fp; 621 { 622 register FILE *dbuf; 623 register int c; 624 char *cp; 625 626 if (fsize(fp) == 0) 627 return; 628 cp = getdeadletter(); 629 c = umask(077); 630 dbuf = Fopen(cp, "a"); 631 (void) umask(c); 632 if (dbuf == NULL) 633 return; 634 while ((c = getc(fp)) != EOF) 635 (void) putc(c, dbuf); 636 Fclose(dbuf); 637 rewind(fp); 638 } 639