xref: /csrg-svn/usr.bin/mail/send.c (revision 42741)
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