Annotation of src/usr.bin/mail/collect.c, Revision 1.1
1.1 ! deraadt 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: }