BSD 4_1c_2 release
[unix-history] / usr / src / ucb / Mail / send.c
#
#include "rcv.h"
#ifdef VMUNIX
#include <wait.h>
#endif
#include <ctype.h>
#include <sys/stat.h>
/*
* Mail -- a mail program
*
* Mail to others.
*/
static char *SccsId = "@(#)send.c 2.10 2/9/83";
/*
* Send message described by the passed pointer to the
* passed output buffer. Return -1 on error, but normally
* the number of lines written. Adjust the status: field
* if need be. If doign is set, suppress ignored header fields.
*/
send(mailp, obuf, doign)
struct message *mailp;
FILE *obuf;
{
register struct message *mp;
register int t;
long c;
FILE *ibuf;
char line[LINESIZE], field[BUFSIZ];
int lc, ishead, infld, fline, dostat;
char *cp, *cp2;
mp = mailp;
ibuf = setinput(mp);
c = mp->m_size;
ishead = 1;
dostat = 1;
infld = 0;
fline = 1;
lc = 0;
while (c > 0L) {
fgets(line, LINESIZE, ibuf);
c -= (long) strlen(line);
lc++;
if (ishead) {
/*
* First line is the From line, so no headers
* there to worry about
*/
if (fline) {
fline = 0;
goto writeit;
}
/*
* If line is blank, we've reached end of
* headers, so force out status: field
* and note that we are no longer in header
* fields
*/
if (line[0] == '\n') {
if (dostat) {
statusput(mailp, obuf, doign);
dostat = 0;
}
ishead = 0;
goto writeit;
}
/*
* If this line is a continuation (via space or tab)
* of a previous header field, just echo it
* (unless the field should be ignored).
*/
if (infld && (isspace(line[0]) || line[0] == '\t')) {
if (doign && isign(field)) continue;
goto writeit;
}
infld = 0;
/*
* If we are no longer looking at real
* header lines, force out status:
* This happens in uucp style mail where
* there are no headers at all.
*/
if (!headerp(line)) {
if (dostat) {
statusput(mailp, obuf, doign);
dostat = 0;
}
putc('\n', obuf);
ishead = 0;
goto writeit;
}
infld++;
/*
* Pick up the header field.
* If it is an ignored field and
* we care about such things, skip it.
*/
cp = line;
cp2 = field;
while (*cp && *cp != ':' && !isspace(*cp))
*cp2++ = *cp++;
*cp2 = 0;
if (doign && isign(field))
continue;
/*
* If the field is "status," go compute and print the
* real Status: field
*/
if (icequal(field, "status")) {
if (dostat) {
statusput(mailp, obuf, doign);
dostat = 0;
}
continue;
}
}
writeit:
fputs(line, obuf);
if (ferror(obuf))
return(-1);
}
if (ferror(obuf))
return(-1);
if (ishead && (mailp->m_flag & MSTATUS))
printf("failed to fix up status field\n");
return(lc);
}
/*
* Test if the passed line is a header line, RFC 733 style.
*/
headerp(line)
register char *line;
{
register char *cp = line;
while (*cp && !isspace(*cp) && *cp != ':')
cp++;
while (*cp && isspace(*cp))
cp++;
return(*cp == ':');
}
/*
* Output a reasonable looking status field.
* But if "status" is ignored and doign, forget it.
*/
statusput(mp, obuf, doign)
register struct message *mp;
register FILE *obuf;
{
char statout[3];
if (doign && isign("status"))
return;
if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
return;
if (mp->m_flag & MREAD)
strcpy(statout, "R");
else
strcpy(statout, "");
if ((mp->m_flag & MNEW) == 0)
strcat(statout, "O");
fprintf(obuf, "Status: %s\n", statout);
}
/*
* Interface between the argument list and the mail1 routine
* which does all the dirty work.
*/
mail(people)
char **people;
{
register char *cp2;
register int s;
char *buf, **ap;
struct header head;
for (s = 0, ap = people; *ap != (char *) -1; ap++)
s += strlen(*ap) + 1;
buf = salloc(s+1);
cp2 = buf;
for (ap = people; *ap != (char *) -1; ap++) {
cp2 = copy(*ap, cp2);
*cp2++ = ' ';
}
if (cp2 != buf)
cp2--;
*cp2 = '\0';
head.h_to = buf;
head.h_subject = NOSTR;
head.h_cc = NOSTR;
head.h_bcc = NOSTR;
head.h_seq = 0;
mail1(&head);
return(0);
}
/*
* Send mail to a bunch of user names. The interface is through
* the mail routine below.
*/
sendmail(str)
char *str;
{
register char **ap;
char *bufp;
register int t;
struct header head;
if (blankline(str))
head.h_to = NOSTR;
else
head.h_to = str;
head.h_subject = NOSTR;
head.h_cc = NOSTR;
head.h_bcc = NOSTR;
head.h_seq = 0;
mail1(&head);
return(0);
}
/*
* Mail a message on standard input to the people indicated
* in the passed header. (Internal interface).
*/
mail1(hp)
struct header *hp;
{
register char *cp;
int pid, i, s, p, gotcha;
char **namelist, *deliver;
struct name *to, *np;
struct stat sbuf;
FILE *mtf, *postage;
int remote = rflag != NOSTR || rmail;
char **t;
/*
* Collect user's mail from standard input.
* Get the result as mtf.
*/
pid = -1;
if ((mtf = collect(hp)) == NULL)
return(-1);
hp->h_seq = 1;
if (hp->h_subject == NOSTR)
hp->h_subject = sflag;
if (fsize(mtf) == 0 && hp->h_subject == NOSTR) {
printf("No message !?!\n");
goto out;
}
if (intty && value("askcc") != NOSTR)
grabh(hp, GCC);
else if (intty) {
printf("EOT\n");
flush();
}
/*
* Now, take the user names from the combined
* to and cc lists and do all the alias
* processing.
*/
senderr = 0;
to = usermap(cat(extract(hp->h_bcc, GBCC),
cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
if (to == NIL) {
printf("No recipients specified\n");
goto topdog;
}
/*
* Look through the recipient list for names with /'s
* in them which we write to as files directly.
*/
to = outof(to, mtf, hp);
rewind(mtf);
to = verify(to);
if (senderr && !remote) {
topdog:
if (fsize(mtf) != 0) {
remove(deadletter);
exwrite(deadletter, mtf, 1);
rewind(mtf);
}
}
for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
if ((np->n_type & GDEL) == 0) {
gotcha++;
break;
}
if (!gotcha)
goto out;
to = elide(to);
mechk(to);
if (count(to) > 1)
hp->h_seq++;
if (hp->h_seq > 0 && !remote) {
fixhead(hp, to);
if (fsize(mtf) == 0)
printf("Null message body; hope that's ok\n");
if ((mtf = infix(hp, mtf)) == NULL) {
fprintf(stderr, ". . . message lost, sorry.\n");
return(-1);
}
}
namelist = unpack(to);
if (debug) {
printf("Recipients of message:\n");
for (t = namelist; *t != NOSTR; t++)
printf(" \"%s\"", *t);
printf("\n");
fflush(stdout);
return;
}
if ((cp = value("record")) != NOSTR)
savemail(expand(cp), hp, mtf);
/*
* Wait, to absorb a potential zombie, then
* fork, set up the temporary mail file as standard
* input for "mail" and exec with the user list we generated
* far above. Return the process id to caller in case he
* wants to await the completion of mail.
*/
#ifdef VMUNIX
#ifdef pdp11
while (wait2(&s, WNOHANG) > 0)
#endif
#if defined(vax) || defined(sun)
while (wait3(&s, WNOHANG, 0) > 0)
#endif
;
#else
wait(&s);
#endif
rewind(mtf);
pid = fork();
if (pid == -1) {
perror("fork");
remove(deadletter);
exwrite(deadletter, mtf, 1);
goto out;
}
if (pid == 0) {
sigchild();
#ifdef SIGTSTP
if (remote == 0) {
sigset(SIGTSTP, SIG_IGN);
sigset(SIGTTIN, SIG_IGN);
sigset(SIGTTOU, SIG_IGN);
}
#endif
for (i = SIGHUP; i <= SIGQUIT; i++)
sigset(i, SIG_IGN);
if (!stat(POSTAGE, &sbuf))
if ((postage = fopen(POSTAGE, "a")) != NULL) {
fprintf(postage, "%s %d %d\n", myname,
count(to), fsize(mtf));
fclose(postage);
}
s = fileno(mtf);
for (i = 3; i < 15; i++)
if (i != s)
close(i);
close(0);
dup(s);
close(s);
#ifdef CC
submit(getpid());
#endif CC
#ifdef SENDMAIL
if ((deliver = value("sendmail")) == NOSTR)
deliver = SENDMAIL;
execv(deliver, namelist);
#endif SENDMAIL
execv(MAIL, namelist);
perror(MAIL);
exit(1);
}
out:
if (remote || (value("verbose") != NOSTR)) {
while ((p = wait(&s)) != pid && p != -1)
;
if (s != 0)
senderr++;
pid = 0;
}
fclose(mtf);
return(pid);
}
/*
* Fix the header by glopping all of the expanded names from
* the distribution list into the appropriate fields.
* If there are any ARPA net recipients in the message,
* we must insert commas, alas.
*/
fixhead(hp, tolist)
struct header *hp;
struct name *tolist;
{
register struct name *nlist;
register int f;
register struct name *np;
for (f = 0, np = tolist; np != NIL; np = np->n_flink)
if (any('@', np->n_name)) {
f |= GCOMMA;
break;
}
if (debug && f & GCOMMA)
fprintf(stderr, "Should be inserting commas in recip lists\n");
hp->h_to = detract(tolist, GTO|f);
hp->h_cc = detract(tolist, GCC|f);
}
/*
* Prepend a header in front of the collected stuff
* and return the new file.
*/
FILE *
infix(hp, fi)
struct header *hp;
FILE *fi;
{
extern char tempMail[];
register FILE *nfo, *nfi;
register int c;
rewind(fi);
if ((nfo = fopen(tempMail, "w")) == NULL) {
perror(tempMail);
return(fi);
}
if ((nfi = fopen(tempMail, "r")) == NULL) {
perror(tempMail);
fclose(nfo);
return(fi);
}
remove(tempMail);
puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
c = getc(fi);
while (c != EOF) {
putc(c, nfo);
c = getc(fi);
}
if (ferror(fi)) {
perror("read");
return(fi);
}
fflush(nfo);
if (ferror(nfo)) {
perror(tempMail);
fclose(nfo);
fclose(nfi);
return(fi);
}
fclose(nfo);
fclose(fi);
rewind(nfi);
return(nfi);
}
/*
* Dump the to, subject, cc header on the
* passed file buffer.
*/
puthead(hp, fo, w)
struct header *hp;
FILE *fo;
{
register int gotcha;
gotcha = 0;
if (hp->h_to != NOSTR && w & GTO)
fmt("To: ", hp->h_to, fo), gotcha++;
if (hp->h_subject != NOSTR && w & GSUBJECT)
fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
if (hp->h_cc != NOSTR && w & GCC)
fmt("Cc: ", hp->h_cc, fo), gotcha++;
if (hp->h_bcc != NOSTR && w & GBCC)
fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
if (gotcha && w & GNL)
putc('\n', fo);
return(0);
}
/*
* Format the given text to not exceed 72 characters.
*/
fmt(str, txt, fo)
register char *str, *txt;
register FILE *fo;
{
register int col;
register char *bg, *bl, *pt, ch;
col = strlen(str);
if (col)
fprintf(fo, "%s", str);
pt = bg = txt;
bl = 0;
while (*bg) {
pt++;
if (++col >72) {
if (!bl) {
bl = bg;
while (*bl && !isspace(*bl))
bl++;
}
if (!*bl)
goto finish;
ch = *bl;
*bl = '\0';
fprintf(fo, "%s\n ", bg);
col = 4;
*bl = ch;
pt = bg = ++bl;
bl = 0;
}
if (!*pt) {
finish:
fprintf(fo, "%s\n", bg);
return;
}
if (isspace(*pt))
bl = pt;
}
}
/*
* Save the outgoing mail on the passed file.
*/
savemail(name, hp, fi)
char name[];
struct header *hp;
FILE *fi;
{
register FILE *fo;
register int c;
long now;
char *n;
if ((fo = fopen(name, "a")) == NULL) {
perror(name);
return(-1);
}
time(&now);
n = rflag;
if (n == NOSTR)
n = myname;
fprintf(fo, "From %s %s", n, ctime(&now));
rewind(fi);
for (c = getc(fi); c != EOF; c = getc(fi))
putc(c, fo);
fprintf(fo, "\n");
fflush(fo);
if (ferror(fo))
perror(name);
fclose(fo);
return(0);
}