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