Annotation of src/usr.bin/mail/send.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: @(#)send.c 8.1 (Berkeley) 6/6/93";
! 36: static char rcsid[] = "$Id: send.c,v 1.5 1994/11/28 20:03:38 jtc Exp $";
! 37: #endif /* not lint */
! 38:
! 39: #include "rcv.h"
! 40: #include "extern.h"
! 41:
! 42: /*
! 43: * Mail -- a mail program
! 44: *
! 45: * Mail to others.
! 46: */
! 47:
! 48: /*
! 49: * Send message described by the passed pointer to the
! 50: * passed output buffer. Return -1 on error.
! 51: * Adjust the status: field if need be.
! 52: * If doign is given, suppress ignored header fields.
! 53: * prefix is a string to prepend to each output line.
! 54: */
! 55: int
! 56: send(mp, obuf, doign, prefix)
! 57: register struct message *mp;
! 58: FILE *obuf;
! 59: struct ignoretab *doign;
! 60: char *prefix;
! 61: {
! 62: long count;
! 63: register FILE *ibuf;
! 64: char line[LINESIZE];
! 65: int ishead, infld, ignoring, dostat, firstline;
! 66: register char *cp, *cp2;
! 67: register int c;
! 68: int length;
! 69: int prefixlen;
! 70:
! 71: /*
! 72: * Compute the prefix string, without trailing whitespace
! 73: */
! 74: if (prefix != NOSTR) {
! 75: cp2 = 0;
! 76: for (cp = prefix; *cp; cp++)
! 77: if (*cp != ' ' && *cp != '\t')
! 78: cp2 = cp;
! 79: prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
! 80: }
! 81: ibuf = setinput(mp);
! 82: count = mp->m_size;
! 83: ishead = 1;
! 84: dostat = doign == 0 || !isign("status", doign);
! 85: infld = 0;
! 86: firstline = 1;
! 87: /*
! 88: * Process headers first
! 89: */
! 90: while (count > 0 && ishead) {
! 91: if (fgets(line, LINESIZE, ibuf) == NULL)
! 92: break;
! 93: count -= length = strlen(line);
! 94: if (firstline) {
! 95: /*
! 96: * First line is the From line, so no headers
! 97: * there to worry about
! 98: */
! 99: firstline = 0;
! 100: ignoring = doign == ignoreall;
! 101: } else if (line[0] == '\n') {
! 102: /*
! 103: * If line is blank, we've reached end of
! 104: * headers, so force out status: field
! 105: * and note that we are no longer in header
! 106: * fields
! 107: */
! 108: if (dostat) {
! 109: statusput(mp, obuf, prefix);
! 110: dostat = 0;
! 111: }
! 112: ishead = 0;
! 113: ignoring = doign == ignoreall;
! 114: } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
! 115: /*
! 116: * If this line is a continuation (via space or tab)
! 117: * of a previous header field, just echo it
! 118: * (unless the field should be ignored).
! 119: * In other words, nothing to do.
! 120: */
! 121: } else {
! 122: /*
! 123: * Pick up the header field if we have one.
! 124: */
! 125: for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
! 126: ;
! 127: cp2 = --cp;
! 128: while (isspace(*cp++))
! 129: ;
! 130: if (cp[-1] != ':') {
! 131: /*
! 132: * Not a header line, force out status:
! 133: * This happens in uucp style mail where
! 134: * there are no headers at all.
! 135: */
! 136: if (dostat) {
! 137: statusput(mp, obuf, prefix);
! 138: dostat = 0;
! 139: }
! 140: if (doign != ignoreall)
! 141: /* add blank line */
! 142: (void) putc('\n', obuf);
! 143: ishead = 0;
! 144: ignoring = 0;
! 145: } else {
! 146: /*
! 147: * If it is an ignored field and
! 148: * we care about such things, skip it.
! 149: */
! 150: *cp2 = 0; /* temporarily null terminate */
! 151: if (doign && isign(line, doign))
! 152: ignoring = 1;
! 153: else if ((line[0] == 's' || line[0] == 'S') &&
! 154: strcasecmp(line, "status") == 0) {
! 155: /*
! 156: * If the field is "status," go compute
! 157: * and print the real Status: field
! 158: */
! 159: if (dostat) {
! 160: statusput(mp, obuf, prefix);
! 161: dostat = 0;
! 162: }
! 163: ignoring = 1;
! 164: } else {
! 165: ignoring = 0;
! 166: *cp2 = c; /* restore */
! 167: }
! 168: infld = 1;
! 169: }
! 170: }
! 171: if (!ignoring) {
! 172: /*
! 173: * Strip trailing whitespace from prefix
! 174: * if line is blank.
! 175: */
! 176: if (prefix != NOSTR)
! 177: if (length > 1)
! 178: fputs(prefix, obuf);
! 179: else
! 180: (void) fwrite(prefix, sizeof *prefix,
! 181: prefixlen, obuf);
! 182: (void) fwrite(line, sizeof *line, length, obuf);
! 183: if (ferror(obuf))
! 184: return -1;
! 185: }
! 186: }
! 187: /*
! 188: * Copy out message body
! 189: */
! 190: if (doign == ignoreall)
! 191: count--; /* skip final blank line */
! 192: if (prefix != NOSTR)
! 193: while (count > 0) {
! 194: if (fgets(line, LINESIZE, ibuf) == NULL) {
! 195: c = 0;
! 196: break;
! 197: }
! 198: count -= c = strlen(line);
! 199: /*
! 200: * Strip trailing whitespace from prefix
! 201: * if line is blank.
! 202: */
! 203: if (c > 1)
! 204: fputs(prefix, obuf);
! 205: else
! 206: (void) fwrite(prefix, sizeof *prefix,
! 207: prefixlen, obuf);
! 208: (void) fwrite(line, sizeof *line, c, obuf);
! 209: if (ferror(obuf))
! 210: return -1;
! 211: }
! 212: else
! 213: while (count > 0) {
! 214: c = count < LINESIZE ? count : LINESIZE;
! 215: if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
! 216: break;
! 217: count -= c;
! 218: if (fwrite(line, sizeof *line, c, obuf) != c)
! 219: return -1;
! 220: }
! 221: if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
! 222: /* no final blank line */
! 223: if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
! 224: return -1;
! 225: return 0;
! 226: }
! 227:
! 228: /*
! 229: * Output a reasonable looking status field.
! 230: */
! 231: void
! 232: statusput(mp, obuf, prefix)
! 233: register struct message *mp;
! 234: FILE *obuf;
! 235: char *prefix;
! 236: {
! 237: char statout[3];
! 238: register char *cp = statout;
! 239:
! 240: if (mp->m_flag & MREAD)
! 241: *cp++ = 'R';
! 242: if ((mp->m_flag & MNEW) == 0)
! 243: *cp++ = 'O';
! 244: *cp = 0;
! 245: if (statout[0])
! 246: fprintf(obuf, "%sStatus: %s\n",
! 247: prefix == NOSTR ? "" : prefix, statout);
! 248: }
! 249:
! 250: /*
! 251: * Interface between the argument list and the mail1 routine
! 252: * which does all the dirty work.
! 253: */
! 254: int
! 255: mail(to, cc, bcc, smopts, subject)
! 256: struct name *to, *cc, *bcc, *smopts;
! 257: char *subject;
! 258: {
! 259: struct header head;
! 260:
! 261: head.h_to = to;
! 262: head.h_subject = subject;
! 263: head.h_cc = cc;
! 264: head.h_bcc = bcc;
! 265: head.h_smopts = smopts;
! 266: mail1(&head, 0);
! 267: return(0);
! 268: }
! 269:
! 270:
! 271: /*
! 272: * Send mail to a bunch of user names. The interface is through
! 273: * the mail routine below.
! 274: */
! 275: int
! 276: sendmail(str)
! 277: char *str;
! 278: {
! 279: struct header head;
! 280:
! 281: head.h_to = extract(str, GTO);
! 282: head.h_subject = NOSTR;
! 283: head.h_cc = NIL;
! 284: head.h_bcc = NIL;
! 285: head.h_smopts = NIL;
! 286: mail1(&head, 0);
! 287: return(0);
! 288: }
! 289:
! 290: /*
! 291: * Mail a message on standard input to the people indicated
! 292: * in the passed header. (Internal interface).
! 293: */
! 294: void
! 295: mail1(hp, printheaders)
! 296: struct header *hp;
! 297: int printheaders;
! 298: {
! 299: char *cp;
! 300: int pid;
! 301: char **namelist;
! 302: struct name *to;
! 303: FILE *mtf;
! 304:
! 305: /*
! 306: * Collect user's mail from standard input.
! 307: * Get the result as mtf.
! 308: */
! 309: if ((mtf = collect(hp, printheaders)) == NULL)
! 310: return;
! 311: if (value("interactive") != NOSTR)
! 312: if (value("askcc") != NOSTR || value("askbcc") != NOSTR) {
! 313: if (value("askcc") != NOSTR)
! 314: grabh(hp, GCC);
! 315: if (value("askbcc") != NOSTR)
! 316: grabh(hp, GBCC);
! 317: } else {
! 318: printf("EOT\n");
! 319: (void) fflush(stdout);
! 320: }
! 321: if (fsize(mtf) == 0)
! 322: if (hp->h_subject == NOSTR)
! 323: printf("No message, no subject; hope that's ok\n");
! 324: else
! 325: printf("Null message body; hope that's ok\n");
! 326: /*
! 327: * Now, take the user names from the combined
! 328: * to and cc lists and do all the alias
! 329: * processing.
! 330: */
! 331: senderr = 0;
! 332: to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
! 333: if (to == NIL) {
! 334: printf("No recipients specified\n");
! 335: senderr++;
! 336: }
! 337: /*
! 338: * Look through the recipient list for names with /'s
! 339: * in them which we write to as files directly.
! 340: */
! 341: to = outof(to, mtf, hp);
! 342: if (senderr)
! 343: savedeadletter(mtf);
! 344: to = elide(to);
! 345: if (count(to) == 0)
! 346: goto out;
! 347: fixhead(hp, to);
! 348: if ((mtf = infix(hp, mtf)) == NULL) {
! 349: fprintf(stderr, ". . . message lost, sorry.\n");
! 350: return;
! 351: }
! 352: namelist = unpack(cat(hp->h_smopts, to));
! 353: if (debug) {
! 354: char **t;
! 355:
! 356: printf("Sendmail arguments:");
! 357: for (t = namelist; *t != NOSTR; t++)
! 358: printf(" \"%s\"", *t);
! 359: printf("\n");
! 360: goto out;
! 361: }
! 362: if ((cp = value("record")) != NOSTR)
! 363: (void) savemail(expand(cp), mtf);
! 364: /*
! 365: * Fork, set up the temporary mail file as standard
! 366: * input for "mail", and exec with the user list we generated
! 367: * far above.
! 368: */
! 369: pid = fork();
! 370: if (pid == -1) {
! 371: perror("fork");
! 372: savedeadletter(mtf);
! 373: goto out;
! 374: }
! 375: if (pid == 0) {
! 376: prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
! 377: sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
! 378: fileno(mtf), -1);
! 379: if ((cp = value("sendmail")) != NOSTR)
! 380: cp = expand(cp);
! 381: else
! 382: cp = _PATH_SENDMAIL;
! 383: execv(cp, namelist);
! 384: perror(cp);
! 385: _exit(1);
! 386: }
! 387: if (value("verbose") != NOSTR)
! 388: (void) wait_child(pid);
! 389: else
! 390: free_child(pid);
! 391: out:
! 392: (void) Fclose(mtf);
! 393: }
! 394:
! 395: /*
! 396: * Fix the header by glopping all of the expanded names from
! 397: * the distribution list into the appropriate fields.
! 398: */
! 399: void
! 400: fixhead(hp, tolist)
! 401: struct header *hp;
! 402: struct name *tolist;
! 403: {
! 404: register struct name *np;
! 405:
! 406: hp->h_to = NIL;
! 407: hp->h_cc = NIL;
! 408: hp->h_bcc = NIL;
! 409: for (np = tolist; np != NIL; np = np->n_flink)
! 410: if ((np->n_type & GMASK) == GTO)
! 411: hp->h_to =
! 412: cat(hp->h_to, nalloc(np->n_name, np->n_type));
! 413: else if ((np->n_type & GMASK) == GCC)
! 414: hp->h_cc =
! 415: cat(hp->h_cc, nalloc(np->n_name, np->n_type));
! 416: else if ((np->n_type & GMASK) == GBCC)
! 417: hp->h_bcc =
! 418: cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
! 419: }
! 420:
! 421: /*
! 422: * Prepend a header in front of the collected stuff
! 423: * and return the new file.
! 424: */
! 425: FILE *
! 426: infix(hp, fi)
! 427: struct header *hp;
! 428: FILE *fi;
! 429: {
! 430: extern char *tempMail;
! 431: register FILE *nfo, *nfi;
! 432: register int c;
! 433:
! 434: if ((nfo = Fopen(tempMail, "w")) == NULL) {
! 435: perror(tempMail);
! 436: return(fi);
! 437: }
! 438: if ((nfi = Fopen(tempMail, "r")) == NULL) {
! 439: perror(tempMail);
! 440: (void) Fclose(nfo);
! 441: return(fi);
! 442: }
! 443: (void) rm(tempMail);
! 444: (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
! 445: c = getc(fi);
! 446: while (c != EOF) {
! 447: (void) putc(c, nfo);
! 448: c = getc(fi);
! 449: }
! 450: if (ferror(fi)) {
! 451: perror("read");
! 452: rewind(fi);
! 453: return(fi);
! 454: }
! 455: (void) fflush(nfo);
! 456: if (ferror(nfo)) {
! 457: perror(tempMail);
! 458: (void) Fclose(nfo);
! 459: (void) Fclose(nfi);
! 460: rewind(fi);
! 461: return(fi);
! 462: }
! 463: (void) Fclose(nfo);
! 464: (void) Fclose(fi);
! 465: rewind(nfi);
! 466: return(nfi);
! 467: }
! 468:
! 469: /*
! 470: * Dump the to, subject, cc header on the
! 471: * passed file buffer.
! 472: */
! 473: int
! 474: puthead(hp, fo, w)
! 475: struct header *hp;
! 476: FILE *fo;
! 477: int w;
! 478: {
! 479: register int gotcha;
! 480:
! 481: gotcha = 0;
! 482: if (hp->h_to != NIL && w & GTO)
! 483: fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
! 484: if (hp->h_subject != NOSTR && w & GSUBJECT)
! 485: fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
! 486: if (hp->h_cc != NIL && w & GCC)
! 487: fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
! 488: if (hp->h_bcc != NIL && w & GBCC)
! 489: fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
! 490: if (gotcha && w & GNL)
! 491: (void) putc('\n', fo);
! 492: return(0);
! 493: }
! 494:
! 495: /*
! 496: * Format the given header line to not exceed 72 characters.
! 497: */
! 498: void
! 499: fmt(str, np, fo, comma)
! 500: char *str;
! 501: register struct name *np;
! 502: FILE *fo;
! 503: int comma;
! 504: {
! 505: register col, len;
! 506:
! 507: comma = comma ? 1 : 0;
! 508: col = strlen(str);
! 509: if (col)
! 510: fputs(str, fo);
! 511: for (; np != NIL; np = np->n_flink) {
! 512: if (np->n_flink == NIL)
! 513: comma = 0;
! 514: len = strlen(np->n_name);
! 515: col++; /* for the space */
! 516: if (col + len + comma > 72 && col > 4) {
! 517: fputs("\n ", fo);
! 518: col = 4;
! 519: } else
! 520: putc(' ', fo);
! 521: fputs(np->n_name, fo);
! 522: if (comma)
! 523: putc(',', fo);
! 524: col += len + comma;
! 525: }
! 526: putc('\n', fo);
! 527: }
! 528:
! 529: /*
! 530: * Save the outgoing mail on the passed file.
! 531: */
! 532:
! 533: /*ARGSUSED*/
! 534: int
! 535: savemail(name, fi)
! 536: char name[];
! 537: register FILE *fi;
! 538: {
! 539: register FILE *fo;
! 540: char buf[BUFSIZ];
! 541: register i;
! 542: time_t now, time();
! 543: char *ctime();
! 544:
! 545: if ((fo = Fopen(name, "a")) == NULL) {
! 546: perror(name);
! 547: return (-1);
! 548: }
! 549: (void) time(&now);
! 550: fprintf(fo, "From %s %s", myname, ctime(&now));
! 551: while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
! 552: (void) fwrite(buf, 1, i, fo);
! 553: (void) putc('\n', fo);
! 554: (void) fflush(fo);
! 555: if (ferror(fo))
! 556: perror(name);
! 557: (void) Fclose(fo);
! 558: rewind(fi);
! 559: return (0);
! 560: }