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