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.4 1994/11/28 20:03:30 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 /* 205 * Escape to command mode, but be nice! 206 */ 207 execute(&linebuf[2], 1); 208 goto cont; 209 case '.': 210 /* 211 * Simulate end of file on input. 212 */ 213 goto out; 214 case 'q': 215 /* 216 * Force a quit of sending mail. 217 * Act like an interrupt happened. 218 */ 219 hadintr++; 220 collint(SIGINT); 221 exit(1); 222 case 'h': 223 /* 224 * Grab a bunch of headers. 225 */ 226 grabh(hp, GTO|GSUBJECT|GCC|GBCC); 227 goto cont; 228 case 't': 229 /* 230 * Add to the To list. 231 */ 232 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 233 break; 234 case 's': 235 /* 236 * Set the Subject list. 237 */ 238 cp = &linebuf[2]; 239 while (isspace(*cp)) 240 cp++; 241 hp->h_subject = savestr(cp); 242 break; 243 case 'c': 244 /* 245 * Add to the CC list. 246 */ 247 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 248 break; 249 case 'b': 250 /* 251 * Add stuff to blind carbon copies list. 252 */ 253 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 254 break; 255 case 'd': 256 strcpy(linebuf + 2, getdeadletter()); 257 /* fall into . . . */ 258 case 'r': 259 /* 260 * Invoke a file: 261 * Search for the file name, 262 * then open it and copy the contents to collf. 263 */ 264 cp = &linebuf[2]; 265 while (isspace(*cp)) 266 cp++; 267 if (*cp == '\0') { 268 printf("Interpolate what file?\n"); 269 break; 270 } 271 cp = expand(cp); 272 if (cp == NOSTR) 273 break; 274 if (isdir(cp)) { 275 printf("%s: Directory\n", cp); 276 break; 277 } 278 if ((fbuf = Fopen(cp, "r")) == NULL) { 279 perror(cp); 280 break; 281 } 282 printf("\"%s\" ", cp); 283 fflush(stdout); 284 lc = 0; 285 cc = 0; 286 while (readline(fbuf, linebuf, LINESIZE) >= 0) { 287 lc++; 288 if ((t = putline(collf, linebuf)) < 0) { 289 Fclose(fbuf); 290 goto err; 291 } 292 cc += t; 293 } 294 Fclose(fbuf); 295 printf("%d/%d\n", lc, cc); 296 break; 297 case 'w': 298 /* 299 * Write the message on a file. 300 */ 301 cp = &linebuf[2]; 302 while (*cp == ' ' || *cp == '\t') 303 cp++; 304 if (*cp == '\0') { 305 fprintf(stderr, "Write what file!?\n"); 306 break; 307 } 308 if ((cp = expand(cp)) == NOSTR) 309 break; 310 rewind(collf); 311 exwrite(cp, collf, 1); 312 break; 313 case 'm': 314 case 'M': 315 case 'f': 316 case 'F': 317 /* 318 * Interpolate the named messages, if we 319 * are in receiving mail mode. Does the 320 * standard list processing garbage. 321 * If ~f is given, we don't shift over. 322 */ 323 if (forward(linebuf + 2, collf, c) < 0) 324 goto err; 325 goto cont; 326 case '?': 327 if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) { 328 perror(_PATH_TILDE); 329 break; 330 } 331 while ((t = getc(fbuf)) != EOF) 332 (void) putchar(t); 333 Fclose(fbuf); 334 break; 335 case 'p': 336 /* 337 * Print out the current state of the 338 * message without altering anything. 339 */ 340 rewind(collf); 341 printf("-------\nMessage contains:\n"); 342 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL); 343 while ((t = getc(collf)) != EOF) 344 (void) putchar(t); 345 goto cont; 346 case '|': 347 /* 348 * Pipe message through command. 349 * Collect output as new message. 350 */ 351 rewind(collf); 352 mespipe(collf, &linebuf[2]); 353 goto cont; 354 case 'v': 355 case 'e': 356 /* 357 * Edit the current message. 358 * 'e' means to use EDITOR 359 * 'v' means to use VISUAL 360 */ 361 rewind(collf); 362 mesedit(collf, c); 363 goto cont; 364 } 365 } 366 goto out; 367 err: 368 if (collf != NULL) { 369 Fclose(collf); 370 collf = NULL; 371 } 372 out: 373 if (collf != NULL) 374 rewind(collf); 375 noreset--; 376 sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); 377 signal(SIGINT, saveint); 378 signal(SIGHUP, savehup); 379 signal(SIGTSTP, savetstp); 380 signal(SIGTTOU, savettou); 381 signal(SIGTTIN, savettin); 382 sigsetmask(omask); 383 return collf; 384 } 385 386 /* 387 * Write a file, ex-like if f set. 388 */ 389 int 390 exwrite(name, fp, f) 391 char name[]; 392 FILE *fp; 393 int f; 394 { 395 register FILE *of; 396 register int c; 397 long cc; 398 int lc; 399 struct stat junk; 400 401 if (f) { 402 printf("\"%s\" ", name); 403 fflush(stdout); 404 } 405 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) { 406 if (!f) 407 fprintf(stderr, "%s: ", name); 408 fprintf(stderr, "File exists\n"); 409 return(-1); 410 } 411 if ((of = Fopen(name, "w")) == NULL) { 412 perror(NOSTR); 413 return(-1); 414 } 415 lc = 0; 416 cc = 0; 417 while ((c = getc(fp)) != EOF) { 418 cc++; 419 if (c == '\n') 420 lc++; 421 (void) putc(c, of); 422 if (ferror(of)) { 423 perror(name); 424 Fclose(of); 425 return(-1); 426 } 427 } 428 Fclose(of); 429 printf("%d/%ld\n", lc, cc); 430 fflush(stdout); 431 return(0); 432 } 433 434 /* 435 * Edit the message being collected on fp. 436 * On return, make the edit file the new temp file. 437 */ 438 void 439 mesedit(fp, c) 440 FILE *fp; 441 int c; 442 { 443 sig_t sigint = signal(SIGINT, SIG_IGN); 444 FILE *nf = run_editor(fp, (off_t)-1, c, 0); 445 446 if (nf != NULL) { 447 fseek(nf, 0L, 2); 448 collf = nf; 449 Fclose(fp); 450 } 451 (void) signal(SIGINT, sigint); 452 } 453 454 /* 455 * Pipe the message through the command. 456 * Old message is on stdin of command; 457 * New message collected from stdout. 458 * Sh -c must return 0 to accept the new message. 459 */ 460 void 461 mespipe(fp, cmd) 462 FILE *fp; 463 char cmd[]; 464 { 465 FILE *nf; 466 sig_t sigint = signal(SIGINT, SIG_IGN); 467 extern char *tempEdit; 468 char *shell; 469 470 if ((nf = Fopen(tempEdit, "w+")) == NULL) { 471 perror(tempEdit); 472 goto out; 473 } 474 (void) unlink(tempEdit); 475 /* 476 * stdin = current message. 477 * stdout = new message. 478 */ 479 if ((shell = value("SHELL")) == NOSTR) 480 shell = _PATH_CSHELL; 481 if (run_command(shell, 482 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) { 483 (void) Fclose(nf); 484 goto out; 485 } 486 if (fsize(nf) == 0) { 487 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 488 (void) Fclose(nf); 489 goto out; 490 } 491 /* 492 * Take new files. 493 */ 494 (void) fseek(nf, 0L, 2); 495 collf = nf; 496 (void) Fclose(fp); 497 out: 498 (void) signal(SIGINT, sigint); 499 } 500 501 /* 502 * Interpolate the named messages into the current 503 * message, preceding each line with a tab. 504 * Return a count of the number of characters now in 505 * the message, or -1 if an error is encountered writing 506 * the message temporary. The flag argument is 'm' if we 507 * should shift over and 'f' if not. 508 */ 509 int 510 forward(ms, fp, f) 511 char ms[]; 512 FILE *fp; 513 int f; 514 { 515 register int *msgvec; 516 extern char *tempMail; 517 struct ignoretab *ig; 518 char *tabst; 519 520 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec); 521 if (msgvec == (int *) NOSTR) 522 return(0); 523 if (getmsglist(ms, msgvec, 0) < 0) 524 return(0); 525 if (*msgvec == 0) { 526 *msgvec = first(0, MMNORM); 527 if (*msgvec == NULL) { 528 printf("No appropriate messages\n"); 529 return(0); 530 } 531 msgvec[1] = NULL; 532 } 533 if (f == 'f' || f == 'F') 534 tabst = NOSTR; 535 else if ((tabst = value("indentprefix")) == NOSTR) 536 tabst = "\t"; 537 ig = isupper(f) ? NULL : ignore; 538 printf("Interpolating:"); 539 for (; *msgvec != 0; msgvec++) { 540 struct message *mp = message + *msgvec - 1; 541 542 touch(mp); 543 printf(" %d", *msgvec); 544 if (send(mp, fp, ig, tabst) < 0) { 545 perror(tempMail); 546 return(-1); 547 } 548 } 549 printf("\n"); 550 return(0); 551 } 552 553 /* 554 * Print (continue) when continued after ^Z. 555 */ 556 /*ARGSUSED*/ 557 void 558 collstop(s) 559 int s; 560 { 561 sig_t old_action = signal(s, SIG_DFL); 562 563 sigsetmask(sigblock(0) & ~sigmask(s)); 564 kill(0, s); 565 sigblock(sigmask(s)); 566 signal(s, old_action); 567 if (colljmp_p) { 568 colljmp_p = 0; 569 hadintr = 0; 570 longjmp(colljmp, 1); 571 } 572 } 573 574 /* 575 * On interrupt, come here to save the partial message in ~/dead.letter. 576 * Then jump out of the collection loop. 577 */ 578 /*ARGSUSED*/ 579 void 580 collint(s) 581 int s; 582 { 583 /* 584 * the control flow is subtle, because we can be called from ~q. 585 */ 586 if (!hadintr) { 587 if (value("ignore") != NOSTR) { 588 puts("@"); 589 fflush(stdout); 590 clearerr(stdin); 591 return; 592 } 593 hadintr = 1; 594 longjmp(colljmp, 1); 595 } 596 rewind(collf); 597 if (value("nosave") == NOSTR) 598 savedeadletter(collf); 599 longjmp(collabort, 1); 600 } 601 602 /*ARGSUSED*/ 603 void 604 collhup(s) 605 int s; 606 { 607 rewind(collf); 608 savedeadletter(collf); 609 /* 610 * Let's pretend nobody else wants to clean up, 611 * a true statement at this time. 612 */ 613 exit(1); 614 } 615 616 void 617 savedeadletter(fp) 618 register FILE *fp; 619 { 620 register FILE *dbuf; 621 register int c; 622 char *cp; 623 624 if (fsize(fp) == 0) 625 return; 626 cp = getdeadletter(); 627 c = umask(077); 628 dbuf = Fopen(cp, "a"); 629 (void) umask(c); 630 if (dbuf == NULL) 631 return; 632 while ((c = getc(fp)) != EOF) 633 (void) putc(c, dbuf); 634 Fclose(dbuf); 635 rewind(fp); 636 } 637