122465Sdist /* 222465Sdist * Copyright (c) 1980 Regents of the University of California. 333499Sbostic * All rights reserved. 433499Sbostic * 5*42741Sbostic * %sccs.include.redist.c% 622465Sdist */ 722465Sdist 834905Sbostic #ifndef lint 9*42741Sbostic static char sccsid[] = "@(#)send.c 5.20 (Berkeley) 06/01/90"; 1034905Sbostic #endif /* not lint */ 111244Skas 121244Skas #include "rcv.h" 131244Skas 141244Skas /* 151244Skas * Mail -- a mail program 161244Skas * 171244Skas * Mail to others. 181244Skas */ 191244Skas 201244Skas /* 211244Skas * Send message described by the passed pointer to the 2234969Sedward * passed output buffer. Return -1 on error. 2334969Sedward * Adjust the status: field if need be. 2434969Sedward * If doign is given, suppress ignored header fields. 2534969Sedward * prefix is a string to prepend to each output line. 261244Skas */ 2734969Sedward send(mp, obuf, doign, prefix) 2831142Sedward register struct message *mp; 291244Skas FILE *obuf; 3034692Sedward struct ignoretab *doign; 3134969Sedward char *prefix; 321244Skas { 3331142Sedward long count; 3431142Sedward register FILE *ibuf; 3531142Sedward char line[LINESIZE]; 3634969Sedward int ishead, infld, ignoring, dostat, firstline; 3731142Sedward register char *cp, *cp2; 3831142Sedward register int c; 3931142Sedward int length; 4039905Sedward int prefixlen; 411244Skas 4239905Sedward /* 4339905Sedward * Compute the prefix string, without trailing whitespace 4439905Sedward */ 4539905Sedward cp2 = 0; 4639905Sedward for (cp = prefix; *cp; cp++) 4739905Sedward if (*cp != ' ' && *cp != '\t') 4839905Sedward cp2 = cp; 4939905Sedward prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; 501244Skas ibuf = setinput(mp); 5131142Sedward count = mp->m_size; 527581Skurt ishead = 1; 5334692Sedward dostat = doign == 0 || !isign("status", doign); 543239Skas infld = 0; 5534969Sedward firstline = 1; 5631142Sedward /* 5731142Sedward * Process headers first 5831142Sedward */ 5931142Sedward while (count > 0 && ishead) { 6031142Sedward if (fgets(line, LINESIZE, ibuf) == NULL) 6131142Sedward break; 6231142Sedward count -= length = strlen(line); 6334969Sedward if (firstline) { 647581Skurt /* 657581Skurt * First line is the From line, so no headers 667581Skurt * there to worry about 677581Skurt */ 6834969Sedward firstline = 0; 6934969Sedward ignoring = doign == ignoreall; 7031142Sedward } else if (line[0] == '\n') { 717581Skurt /* 727581Skurt * If line is blank, we've reached end of 737581Skurt * headers, so force out status: field 747581Skurt * and note that we are no longer in header 757581Skurt * fields 767581Skurt */ 7731142Sedward if (dostat) { 7834969Sedward statusput(mp, obuf, prefix); 7931142Sedward dostat = 0; 801487Skas } 8131142Sedward ishead = 0; 8234969Sedward ignoring = doign == ignoreall; 8331142Sedward } else if (infld && (line[0] == ' ' || line[0] == '\t')) { 847581Skurt /* 859290Sbush * If this line is a continuation (via space or tab) 869290Sbush * of a previous header field, just echo it 879290Sbush * (unless the field should be ignored). 8831142Sedward * In other words, nothing to do. 897581Skurt */ 9031142Sedward } else { 917581Skurt /* 9231142Sedward * Pick up the header field if we have one. 937581Skurt */ 9431142Sedward for (cp = line; (c = *cp++) && c != ':' && !isspace(c);) 9531142Sedward ; 9631142Sedward cp2 = --cp; 9731142Sedward while (isspace(*cp++)) 9831142Sedward ; 9931142Sedward if (cp[-1] != ':') { 10031142Sedward /* 10131142Sedward * Not a header line, force out status: 10231142Sedward * This happens in uucp style mail where 10331142Sedward * there are no headers at all. 10431142Sedward */ 1057581Skurt if (dostat) { 10634969Sedward statusput(mp, obuf, prefix); 1077581Skurt dostat = 0; 1087581Skurt } 10934969Sedward if (doign != ignoreall) 11034969Sedward /* add blank line */ 11134969Sedward (void) putc('\n', obuf); 1123239Skas ishead = 0; 11331142Sedward ignoring = 0; 11431142Sedward } else { 11531142Sedward /* 11631142Sedward * If it is an ignored field and 11731142Sedward * we care about such things, skip it. 11831142Sedward */ 11931142Sedward *cp2 = 0; /* temporarily null terminate */ 12034692Sedward if (doign && isign(line, doign)) 12131142Sedward ignoring = 1; 12231142Sedward else if ((line[0] == 's' || line[0] == 'S') && 12334987Sedward strcasecmp(line, "status") == 0) { 12431142Sedward /* 12531142Sedward * If the field is "status," go compute 12631142Sedward * and print the real Status: field 12731142Sedward */ 12831142Sedward if (dostat) { 12934969Sedward statusput(mp, obuf, prefix); 13031142Sedward dostat = 0; 13131142Sedward } 13231142Sedward ignoring = 1; 13331142Sedward } else { 13431142Sedward ignoring = 0; 13531142Sedward *cp2 = c; /* restore */ 1367583Skurt } 13731142Sedward infld = 1; 1387583Skurt } 1391487Skas } 14031142Sedward if (!ignoring) { 14139905Sedward /* 14239905Sedward * Strip trailing whitespace from prefix 14339905Sedward * if line is blank. 14439905Sedward */ 14539905Sedward if (prefix != NOSTR) 14639905Sedward if (length > 1) 14739905Sedward fputs(prefix, obuf); 14839905Sedward else 14939905Sedward (void) fwrite(prefix, sizeof *prefix, 15039905Sedward prefixlen, obuf); 15131143Sedward (void) fwrite(line, sizeof *line, length, obuf); 15231142Sedward if (ferror(obuf)) 15331142Sedward return -1; 15431142Sedward } 1551244Skas } 15631142Sedward /* 15731142Sedward * Copy out message body 15831142Sedward */ 15934969Sedward if (doign == ignoreall) 16034969Sedward count--; /* skip final blank line */ 16134969Sedward if (prefix != NOSTR) 16234969Sedward while (count > 0) { 16334969Sedward if (fgets(line, LINESIZE, ibuf) == NULL) { 16434969Sedward c = 0; 16534969Sedward break; 16634969Sedward } 16734969Sedward count -= c = strlen(line); 16839905Sedward /* 16939905Sedward * Strip trailing whitespace from prefix 17039905Sedward * if line is blank. 17139905Sedward */ 17234969Sedward if (c > 1) 17334969Sedward fputs(prefix, obuf); 17439905Sedward else 17539905Sedward (void) fwrite(prefix, sizeof *prefix, 17639905Sedward prefixlen, obuf); 17734969Sedward (void) fwrite(line, sizeof *line, c, obuf); 17834969Sedward if (ferror(obuf)) 17934969Sedward return -1; 18034969Sedward } 18134969Sedward else 18234969Sedward while (count > 0) { 18334969Sedward c = count < LINESIZE ? count : LINESIZE; 18434969Sedward if ((c = fread(line, sizeof *line, c, ibuf)) <= 0) 18534969Sedward break; 18634969Sedward count -= c; 18734969Sedward if (fwrite(line, sizeof *line, c, obuf) != c) 18834969Sedward return -1; 18934969Sedward } 19034969Sedward if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 19134969Sedward /* no final blank line */ 19234969Sedward if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 19331142Sedward return -1; 19434969Sedward return 0; 1951244Skas } 1961244Skas 1971244Skas /* 1981487Skas * Output a reasonable looking status field. 1991487Skas */ 20034969Sedward statusput(mp, obuf, prefix) 2011487Skas register struct message *mp; 20231142Sedward FILE *obuf; 20334969Sedward char *prefix; 2041487Skas { 2051487Skas char statout[3]; 20631142Sedward register char *cp = statout; 2071487Skas 2081487Skas if (mp->m_flag & MREAD) 20931142Sedward *cp++ = 'R'; 2101487Skas if ((mp->m_flag & MNEW) == 0) 21131142Sedward *cp++ = 'O'; 21231142Sedward *cp = 0; 21331142Sedward if (statout[0]) 21434969Sedward fprintf(obuf, "%sStatus: %s\n", 21534969Sedward prefix == NOSTR ? "" : prefix, statout); 2161487Skas } 2171487Skas 2181487Skas /* 2191244Skas * Interface between the argument list and the mail1 routine 2201244Skas * which does all the dirty work. 2211244Skas */ 22234800Sedward mail(to, cc, bcc, smopts, subject) 22334702Sedward struct name *to, *cc, *bcc, *smopts; 22434800Sedward char *subject; 2251244Skas { 2261244Skas struct header head; 2271244Skas 22834800Sedward head.h_to = to; 22934800Sedward head.h_subject = subject; 23034800Sedward head.h_cc = cc; 23134800Sedward head.h_bcc = bcc; 23234800Sedward head.h_smopts = smopts; 23334976Sedward mail1(&head, 0); 2341244Skas return(0); 2351244Skas } 2361244Skas 2371244Skas 2381244Skas /* 2391244Skas * Send mail to a bunch of user names. The interface is through 2401244Skas * the mail routine below. 2411244Skas */ 2421244Skas sendmail(str) 2431244Skas char *str; 2441244Skas { 2451244Skas struct header head; 2461244Skas 24734800Sedward head.h_to = extract(str, GTO); 2481244Skas head.h_subject = NOSTR; 24934800Sedward head.h_cc = NIL; 25034800Sedward head.h_bcc = NIL; 25134800Sedward head.h_smopts = NIL; 25234976Sedward mail1(&head, 0); 2531244Skas return(0); 2541244Skas } 2551244Skas 2561244Skas /* 2571244Skas * Mail a message on standard input to the people indicated 2581244Skas * in the passed header. (Internal interface). 2591244Skas */ 26034800Sedward mail1(hp, printheaders) 2611244Skas struct header *hp; 2621244Skas { 26334976Sedward char *cp; 26434976Sedward int pid; 26534976Sedward char **namelist; 26634976Sedward struct name *to; 26734976Sedward FILE *mtf; 2681244Skas 2691244Skas /* 2701244Skas * Collect user's mail from standard input. 2711244Skas * Get the result as mtf. 2721244Skas */ 27334800Sedward if ((mtf = collect(hp, printheaders)) == NULL) 27434976Sedward return; 27534750Sedward if (value("interactive") != NOSTR) 27634750Sedward if (value("askcc") != NOSTR) 27734750Sedward grabh(hp, GCC); 27834750Sedward else { 27934750Sedward printf("EOT\n"); 28034750Sedward (void) fflush(stdout); 28134750Sedward } 28234976Sedward if (fsize(mtf) == 0) 28334976Sedward if (hp->h_subject == NOSTR) 28434976Sedward printf("No message, no subject; hope that's ok\n"); 28534976Sedward else 28634976Sedward printf("Null message body; hope that's ok\n"); 2871244Skas /* 2881244Skas * Now, take the user names from the combined 2891244Skas * to and cc lists and do all the alias 2901244Skas * processing. 2911244Skas */ 2921244Skas senderr = 0; 29334800Sedward to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 2941244Skas if (to == NIL) { 2951244Skas printf("No recipients specified\n"); 29634976Sedward senderr++; 2971244Skas } 2981244Skas /* 2991244Skas * Look through the recipient list for names with /'s 3001244Skas * in them which we write to as files directly. 3011244Skas */ 3021244Skas to = outof(to, mtf, hp); 30335351Sedward if (senderr) 30435351Sedward savedeadletter(mtf); 30534976Sedward to = elide(to); 30634976Sedward if (count(to) == 0) 3071244Skas goto out; 30834976Sedward fixhead(hp, to); 30934976Sedward if ((mtf = infix(hp, mtf)) == NULL) { 31034976Sedward fprintf(stderr, ". . . message lost, sorry.\n"); 31134976Sedward return; 3121244Skas } 31334800Sedward namelist = unpack(cat(hp->h_smopts, to)); 3141244Skas if (debug) { 31534976Sedward char **t; 31634976Sedward 31734800Sedward printf("Sendmail arguments:"); 3181244Skas for (t = namelist; *t != NOSTR; t++) 3191244Skas printf(" \"%s\"", *t); 3201244Skas printf("\n"); 32134976Sedward goto out; 3221244Skas } 3231244Skas if ((cp = value("record")) != NOSTR) 32431143Sedward (void) savemail(expand(cp), mtf); 3251244Skas /* 32634976Sedward * Fork, set up the temporary mail file as standard 32734976Sedward * input for "mail", and exec with the user list we generated 32834976Sedward * far above. 3291244Skas */ 3301244Skas pid = fork(); 3311244Skas if (pid == -1) { 3321244Skas perror("fork"); 33335351Sedward savedeadletter(mtf); 3341244Skas goto out; 3351244Skas } 3361244Skas if (pid == 0) { 33737870Sbostic if (access(_PATH_MAIL_LOG, 0) == 0) { 33834976Sedward FILE *postage; 33934976Sedward 34037870Sbostic if ((postage = fopen(_PATH_MAIL_LOG, "a")) != NULL) { 34131143Sedward fprintf(postage, "%s %d %ld\n", myname, 34210580Sleres count(to), fsize(mtf)); 34331143Sedward (void) fclose(postage); 34410580Sleres } 34534976Sedward } 34634976Sedward prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)| 34734976Sedward sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU), 34834976Sedward fileno(mtf), -1); 34934976Sedward if ((cp = value("sendmail")) != NOSTR) 35034976Sedward cp = expand(cp); 35134976Sedward else 35237870Sbostic cp = _PATH_SENDMAIL; 35334976Sedward execv(cp, namelist); 35434976Sedward perror(cp); 35535066Sedward _exit(1); 3561244Skas } 35734976Sedward if (value("verbose") != NOSTR) 35834976Sedward (void) wait_child(pid); 35934976Sedward else 36034976Sedward free_child(pid); 3611244Skas out: 36231143Sedward (void) fclose(mtf); 3631244Skas } 3641244Skas 3651244Skas /* 3661244Skas * Fix the header by glopping all of the expanded names from 3671244Skas * the distribution list into the appropriate fields. 3681244Skas */ 3691244Skas fixhead(hp, tolist) 3701244Skas struct header *hp; 3711244Skas struct name *tolist; 3721244Skas { 3731244Skas register struct name *np; 3741244Skas 37534800Sedward hp->h_to = NIL; 37634800Sedward hp->h_cc = NIL; 37734800Sedward hp->h_bcc = NIL; 37834800Sedward for (np = tolist; np != NIL; np = np->n_flink) 37934800Sedward if ((np->n_type & GMASK) == GTO) 38034800Sedward hp->h_to = 38134800Sedward cat(hp->h_to, nalloc(np->n_name, np->n_type)); 38234800Sedward else if ((np->n_type & GMASK) == GCC) 38334800Sedward hp->h_cc = 38434800Sedward cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 38534800Sedward else if ((np->n_type & GMASK) == GBCC) 38634800Sedward hp->h_bcc = 38734800Sedward cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 3881244Skas } 3891244Skas 3901244Skas /* 3911244Skas * Prepend a header in front of the collected stuff 3921244Skas * and return the new file. 3931244Skas */ 3941244Skas FILE * 3951244Skas infix(hp, fi) 3961244Skas struct header *hp; 3971244Skas FILE *fi; 3981244Skas { 3991244Skas extern char tempMail[]; 4001244Skas register FILE *nfo, *nfi; 4011244Skas register int c; 4021244Skas 4031244Skas if ((nfo = fopen(tempMail, "w")) == NULL) { 4041244Skas perror(tempMail); 4051244Skas return(fi); 4061244Skas } 4071244Skas if ((nfi = fopen(tempMail, "r")) == NULL) { 4081244Skas perror(tempMail); 40931143Sedward (void) fclose(nfo); 4101244Skas return(fi); 4111244Skas } 41231143Sedward (void) remove(tempMail); 41334800Sedward (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); 4141244Skas c = getc(fi); 4151244Skas while (c != EOF) { 41631143Sedward (void) putc(c, nfo); 4171244Skas c = getc(fi); 4181244Skas } 4191244Skas if (ferror(fi)) { 4201244Skas perror("read"); 42134976Sedward rewind(fi); 4221244Skas return(fi); 4231244Skas } 42431143Sedward (void) fflush(nfo); 4251244Skas if (ferror(nfo)) { 4261244Skas perror(tempMail); 42731143Sedward (void) fclose(nfo); 42831143Sedward (void) fclose(nfi); 42934976Sedward rewind(fi); 4301244Skas return(fi); 4311244Skas } 43231143Sedward (void) fclose(nfo); 43331143Sedward (void) fclose(fi); 4341244Skas rewind(nfi); 4351244Skas return(nfi); 4361244Skas } 4371244Skas 4381244Skas /* 4391244Skas * Dump the to, subject, cc header on the 4401244Skas * passed file buffer. 4411244Skas */ 4421244Skas puthead(hp, fo, w) 4431244Skas struct header *hp; 4441244Skas FILE *fo; 4451244Skas { 4461244Skas register int gotcha; 4471244Skas 4481244Skas gotcha = 0; 44934800Sedward if (hp->h_to != NIL && w & GTO) 45034976Sedward fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 4511244Skas if (hp->h_subject != NOSTR && w & GSUBJECT) 4521244Skas fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 45334800Sedward if (hp->h_cc != NIL && w & GCC) 45434976Sedward fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 45534800Sedward if (hp->h_bcc != NIL && w & GBCC) 45634976Sedward fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 4571244Skas if (gotcha && w & GNL) 45831143Sedward (void) putc('\n', fo); 4591244Skas return(0); 4601244Skas } 4611244Skas 4621244Skas /* 46334976Sedward * Format the given header line to not exceed 72 characters. 4641244Skas */ 46534800Sedward fmt(str, np, fo, comma) 46634800Sedward char *str; 46734800Sedward register struct name *np; 46834800Sedward FILE *fo; 46934800Sedward int comma; 4701244Skas { 47134800Sedward register col, len; 4721244Skas 47334800Sedward comma = comma ? 1 : 0; 47410580Sleres col = strlen(str); 47510580Sleres if (col) 47634800Sedward fputs(str, fo); 47734976Sedward for (; np != NIL; np = np->n_flink) { 47834800Sedward if (np->n_flink == NIL) 47934800Sedward comma = 0; 48034800Sedward len = strlen(np->n_name); 48134976Sedward col++; /* for the space */ 48234976Sedward if (col + len + comma > 72 && col > 4) { 48334800Sedward fputs("\n ", fo); 4841244Skas col = 4; 48534976Sedward } else 48634800Sedward putc(' ', fo); 48734976Sedward fputs(np->n_name, fo); 48834976Sedward if (comma) 48934976Sedward putc(',', fo); 49034976Sedward col += len + comma; 4911244Skas } 49234800Sedward putc('\n', fo); 4931244Skas } 4941244Skas 4951244Skas /* 4961244Skas * Save the outgoing mail on the passed file. 4971244Skas */ 4981244Skas 49931142Sedward /*ARGSUSED*/ 50031142Sedward savemail(name, fi) 5011244Skas char name[]; 50231142Sedward register FILE *fi; 5031244Skas { 5041244Skas register FILE *fo; 50531142Sedward char buf[BUFSIZ]; 50631142Sedward register i; 50731142Sedward time_t now, time(); 50831142Sedward char *ctime(); 5091244Skas 5101244Skas if ((fo = fopen(name, "a")) == NULL) { 5111244Skas perror(name); 51231142Sedward return (-1); 5131244Skas } 51431143Sedward (void) time(&now); 51534783Sedward fprintf(fo, "From %s %s", myname, ctime(&now)); 51631142Sedward while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 51731143Sedward (void) fwrite(buf, 1, i, fo); 51831143Sedward (void) putc('\n', fo); 51931143Sedward (void) fflush(fo); 5201244Skas if (ferror(fo)) 5211244Skas perror(name); 52231143Sedward (void) fclose(fo); 52334976Sedward rewind(fi); 52431142Sedward return (0); 5251244Skas } 526