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