xref: /csrg-svn/usr.bin/mail/send.c (revision 3239)
11244Skas #
21244Skas 
31244Skas #include "rcv.h"
41244Skas #ifdef VMUNIX
51244Skas #include <wait.h>
61244Skas #endif
7*3239Skas #include <ctype.h>
81244Skas 
91244Skas /*
101244Skas  * Mail -- a mail program
111244Skas  *
121244Skas  * Mail to others.
131244Skas  */
141244Skas 
15*3239Skas static char *SccsId = "@(#)send.c	1.4 03/13/81";
161244Skas 
171244Skas /*
181244Skas  * Send message described by the passed pointer to the
191244Skas  * passed output buffer.  Return -1 on error, but normally
201487Skas  * the number of lines written.  Adjust the status: field
211487Skas  * if need be.
221244Skas  */
231244Skas 
241244Skas send(mailp, obuf)
251244Skas 	struct message *mailp;
261244Skas 	FILE *obuf;
271244Skas {
281244Skas 	register struct message *mp;
291244Skas 	register int t;
301244Skas 	unsigned int c;
311244Skas 	FILE *ibuf;
321487Skas 	char line[LINESIZE];
33*3239Skas 	int lc, ishead, infld, fline;
341244Skas 
351244Skas 	mp = mailp;
361244Skas 	ibuf = setinput(mp);
371244Skas 	c = msize(mp);
381487Skas 	ishead = (mailp->m_flag & MSTATUS) != 0;
39*3239Skas 	infld = 0;
40*3239Skas 	fline = 1;
411244Skas 	lc = 0;
421487Skas 	while (c > 0) {
431487Skas 		fgets(line, LINESIZE, ibuf);
441487Skas 		c -= strlen(line);
451487Skas 		lc++;
461487Skas 		if (ishead) {
47*3239Skas 			if (fline) {
48*3239Skas 				fline = 0;
49*3239Skas 				goto writeit;
50*3239Skas 			}
511487Skas 			if (line[0] == '\n') {
521487Skas 				statusput(mailp, obuf);
531487Skas 				ishead = 0;
541487Skas 				goto writeit;
551487Skas 			}
56*3239Skas 			if (isspace(line[0]) && infld)
571487Skas 				goto writeit;
58*3239Skas 			infld = 0;
59*3239Skas 			if (!headerp(line)) {
60*3239Skas 				statusput(mailp, obuf);
61*3239Skas 				putc('\n', obuf);
62*3239Skas 				ishead = 0;
63*3239Skas 				goto writeit;
64*3239Skas 			}
65*3239Skas 			infld++;
661487Skas 			if (icisname(line, "status", 6)) {
671487Skas 				statusput(mailp, obuf);
681487Skas 				ishead = 0;
691487Skas 				continue;
701487Skas 			}
711487Skas 		}
721487Skas writeit:
731487Skas 		fputs(line, obuf);
741244Skas 		if (ferror(obuf))
751244Skas 			return(-1);
761244Skas 	}
771487Skas 	if (ferror(obuf))
781487Skas 		return(-1);
791487Skas 	if (ishead && (mailp->m_flag & MSTATUS))
801487Skas 		printf("failed to fix up status field\n");
811244Skas 	return(lc);
821244Skas }
831244Skas 
841244Skas /*
85*3239Skas  * Test if the passed line is a header line, RFC 733 style.
86*3239Skas  */
87*3239Skas headerp(line)
88*3239Skas 	register char *line;
89*3239Skas {
90*3239Skas 	register char *cp = line;
91*3239Skas 
92*3239Skas 	while (*cp && !isspace(*cp) && *cp != ':')
93*3239Skas 		cp++;
94*3239Skas 	while (*cp && isspace(*cp))
95*3239Skas 		cp++;
96*3239Skas 	return(*cp == ':');
97*3239Skas }
98*3239Skas 
99*3239Skas /*
1001487Skas  * Output a reasonable looking status field.
1011487Skas  */
1021487Skas 
1031487Skas statusput(mp, obuf)
1041487Skas 	register struct message *mp;
1051487Skas 	register FILE *obuf;
1061487Skas {
1071487Skas 	char statout[3];
1081487Skas 
1091487Skas 	if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
1101487Skas 		return;
1111487Skas 	if (mp->m_flag & MREAD)
1121487Skas 		strcpy(statout, "R");
1131487Skas 	else
1141487Skas 		strcpy(statout, "");
1151487Skas 	if ((mp->m_flag & MNEW) == 0)
1161487Skas 		strcat(statout, "O");
1171487Skas 	fprintf(obuf, "Status: %s\n", statout);
1181487Skas }
1191487Skas 
1201487Skas 
1211487Skas /*
1221244Skas  * Interface between the argument list and the mail1 routine
1231244Skas  * which does all the dirty work.
1241244Skas  */
1251244Skas 
1261244Skas mail(people)
1271244Skas 	char **people;
1281244Skas {
1291244Skas 	register char *cp2;
1301244Skas 	register int s;
1311244Skas 	char *buf, **ap;
1321244Skas 	struct header head;
1331244Skas 
1341244Skas 	for (s = 0, ap = people; *ap != (char *) -1; ap++)
1351244Skas 		s += strlen(*ap) + 1;
1361244Skas 	buf = salloc(s+1);
1371244Skas 	cp2 = buf;
1381244Skas 	for (ap = people; *ap != (char *) -1; ap++) {
1391244Skas 		cp2 = copy(*ap, cp2);
1401244Skas 		*cp2++ = ' ';
1411244Skas 	}
1421244Skas 	if (cp2 != buf)
1431244Skas 		cp2--;
1441244Skas 	*cp2 = '\0';
1451244Skas 	head.h_to = buf;
1461244Skas 	head.h_subject = NOSTR;
1471244Skas 	head.h_cc = NOSTR;
1481244Skas 	head.h_bcc = NOSTR;
1491244Skas 	head.h_seq = 0;
1501244Skas 	mail1(&head);
1511244Skas 	return(0);
1521244Skas }
1531244Skas 
1541244Skas 
1551244Skas /*
1561244Skas  * Send mail to a bunch of user names.  The interface is through
1571244Skas  * the mail routine below.
1581244Skas  */
1591244Skas 
1601244Skas sendmail(str)
1611244Skas 	char *str;
1621244Skas {
1631244Skas 	register char **ap;
1641244Skas 	char *bufp;
1651244Skas 	register int t;
1661244Skas 	struct header head;
1671244Skas 
1681244Skas 	if (blankline(str))
1691244Skas 		head.h_to = NOSTR;
1701244Skas 	else
1711244Skas 		head.h_to = str;
1721244Skas 	head.h_subject = NOSTR;
1731244Skas 	head.h_cc = NOSTR;
1741244Skas 	head.h_bcc = NOSTR;
1751244Skas 	head.h_seq = 0;
1761244Skas 	mail1(&head);
1771244Skas 	return(0);
1781244Skas }
1791244Skas 
1801244Skas /*
1811244Skas  * Mail a message on standard input to the people indicated
1821244Skas  * in the passed header.  (Internal interface).
1831244Skas  */
1841244Skas 
1851244Skas mail1(hp)
1861244Skas 	struct header *hp;
1871244Skas {
1881244Skas 	register char *cp;
1891244Skas 	int pid, i, s, p, gotcha;
1901244Skas 	char **namelist;
1911244Skas 	struct name *to, *np;
1921244Skas 	FILE *mtf, *postage;
1931244Skas 	int remote = rflag != NOSTR || rmail;
1941244Skas 	char **t;
1951244Skas 
1961244Skas 	/*
1971244Skas 	 * Collect user's mail from standard input.
1981244Skas 	 * Get the result as mtf.
1991244Skas 	 */
2001244Skas 
2011244Skas 	pid = -1;
2021244Skas 	if ((mtf = collect(hp)) == NULL)
2031244Skas 		return(-1);
2041244Skas 	hp->h_seq = 1;
2051244Skas 	if (hp->h_subject == NOSTR)
2061244Skas 		hp->h_subject = sflag;
2071244Skas 	if (fsize(mtf) == 0 && hp->h_subject == NOSTR) {
2081244Skas 		printf("No message !?!\n");
2091244Skas 		goto out;
2101244Skas 	}
2111244Skas 	if (intty && value("askcc") != NOSTR)
2121244Skas 		grabh(hp, GCC);
2131244Skas 	else if (intty) {
2141244Skas 		printf("EOT\n");
2151244Skas 		flush();
2161244Skas 	}
2171244Skas 
2181244Skas 	/*
2191244Skas 	 * Now, take the user names from the combined
2201244Skas 	 * to and cc lists and do all the alias
2211244Skas 	 * processing.
2221244Skas 	 */
2231244Skas 
2241244Skas 	senderr = 0;
2251244Skas 	to = usermap(cat(extract(hp->h_bcc, GBCC),
2261244Skas 	    cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
2271244Skas 	if (to == NIL) {
2281244Skas 		printf("No recipients specified\n");
2291244Skas 		goto topdog;
2301244Skas 	}
2311244Skas 
2321244Skas 	/*
2331244Skas 	 * Look through the recipient list for names with /'s
2341244Skas 	 * in them which we write to as files directly.
2351244Skas 	 */
2361244Skas 
2371244Skas 	to = outof(to, mtf, hp);
2381244Skas 	rewind(mtf);
2391244Skas 	to = verify(to);
2401244Skas 	if (senderr && !remote) {
2411244Skas topdog:
2421244Skas 
2431244Skas 		if (fsize(mtf) != 0) {
2441244Skas 			remove(deadletter);
2451244Skas 			exwrite(deadletter, mtf, 1);
2461244Skas 			rewind(mtf);
2471244Skas 		}
2481244Skas 	}
2491244Skas 	for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
2501244Skas 		if ((np->n_type & GDEL) == 0) {
2511244Skas 			gotcha++;
2521244Skas 			break;
2531244Skas 		}
2541244Skas 	if (!gotcha)
2551244Skas 		goto out;
2561244Skas 	to = elide(to);
2571244Skas 	mechk(to);
2581244Skas 	if (count(to) > 1)
2591244Skas 		hp->h_seq++;
2601244Skas 	if (hp->h_seq > 0 && !remote) {
2611244Skas 		fixhead(hp, to);
2621244Skas 		if (fsize(mtf) == 0)
2631244Skas 			printf("Null message body; hope that's ok\n");
2641244Skas 		if ((mtf = infix(hp, mtf)) == NULL) {
2651244Skas 			fprintf(stderr, ". . . message lost, sorry.\n");
2661244Skas 			return(-1);
2671244Skas 		}
2681244Skas 	}
2691244Skas 	namelist = unpack(to);
2701244Skas 	if (debug) {
2711244Skas 		printf("Recipients of message:\n");
2721244Skas 		for (t = namelist; *t != NOSTR; t++)
2731244Skas 			printf(" \"%s\"", *t);
2741244Skas 		printf("\n");
2751244Skas 		fflush(stdout);
2761244Skas 		return;
2771244Skas 	}
2781244Skas 	if ((cp = value("record")) != NOSTR)
2791244Skas 		savemail(expand(cp), hp, mtf);
2801244Skas 
2811244Skas 	/*
2821244Skas 	 * Wait, to absorb a potential zombie, then
2831244Skas 	 * fork, set up the temporary mail file as standard
2841244Skas 	 * input for "mail" and exec with the user list we generated
2851244Skas 	 * far above. Return the process id to caller in case he
2861244Skas 	 * wants to await the completion of mail.
2871244Skas 	 */
2881244Skas 
2891244Skas #ifdef VMUNIX
2901244Skas 	while (wait3(&s, WNOHANG, 0) > 0)
2911244Skas 		;
2921244Skas #else
2931244Skas 	wait(&s);
2941244Skas #endif
2951244Skas 	rewind(mtf);
2961244Skas 	pid = fork();
2971244Skas 	if (pid == -1) {
2981244Skas 		perror("fork");
2991244Skas 		remove(deadletter);
3001244Skas 		exwrite(deadletter, mtf, 1);
3011244Skas 		goto out;
3021244Skas 	}
3031244Skas 	if (pid == 0) {
3041244Skas #ifdef SIGTSTP
3051244Skas 		if (remote == 0) {
3061244Skas 			signal(SIGTSTP, SIG_IGN);
3071244Skas 			signal(SIGTTIN, SIG_IGN);
3081244Skas 			signal(SIGTTOU, SIG_IGN);
3091244Skas 		}
3101244Skas #endif
3111244Skas 		for (i = SIGHUP; i <= SIGQUIT; i++)
3121244Skas 			signal(i, SIG_IGN);
3131244Skas 		if ((postage = fopen("/crp/kurt/postage", "a")) != NULL) {
3141244Skas 			fprintf(postage, "%s %d %d\n", myname,
3151244Skas 			    count(to), fsize(mtf));
3161244Skas 			fclose(postage);
3171244Skas 		}
3181244Skas 		s = fileno(mtf);
3191244Skas 		for (i = 3; i < 15; i++)
3201244Skas 			if (i != s)
3211244Skas 				close(i);
3221244Skas 		close(0);
3231244Skas 		dup(s);
3241244Skas 		close(s);
3251244Skas #ifdef CC
3261244Skas 		submit(getpid());
3271244Skas #endif CC
3281244Skas #ifdef DELIVERMAIL
3291244Skas 		execv(DELIVERMAIL, namelist);
3301244Skas #endif DELIVERMAIL
3311244Skas 		execv(MAIL, namelist);
3321244Skas 		perror(MAIL);
3331244Skas 		exit(1);
3341244Skas 	}
3351244Skas 
3361244Skas out:
3371244Skas 	if (remote) {
3381244Skas 		while ((p = wait(&s)) != pid && p != -1)
3391244Skas 			;
3401244Skas 		if (s != 0)
3411244Skas 			senderr++;
3421244Skas 		pid = 0;
3431244Skas 	}
3441244Skas 	fclose(mtf);
3451244Skas 	return(pid);
3461244Skas }
3471244Skas 
3481244Skas /*
3491244Skas  * Fix the header by glopping all of the expanded names from
3501244Skas  * the distribution list into the appropriate fields.
3511244Skas  * If there are any ARPA net recipients in the message,
3521244Skas  * we must insert commas, alas.
3531244Skas  */
3541244Skas 
3551244Skas fixhead(hp, tolist)
3561244Skas 	struct header *hp;
3571244Skas 	struct name *tolist;
3581244Skas {
3591244Skas 	register struct name *nlist;
3601244Skas 	register int f;
3611244Skas 	register struct name *np;
3621244Skas 
3631244Skas 	for (f = 0, np = tolist; np != NIL; np = np->n_flink)
3641244Skas 		if (any('@', np->n_name)) {
3651244Skas 			f |= GCOMMA;
3661244Skas 			break;
3671244Skas 		}
3681244Skas 
3691244Skas 	if (debug && f & GCOMMA)
3701244Skas 		fprintf(stderr, "Should be inserting commas in recip lists\n");
3711244Skas 	hp->h_to = detract(tolist, GTO|f);
3721244Skas 	hp->h_cc = detract(tolist, GCC|f);
3731244Skas }
3741244Skas 
3751244Skas /*
3761244Skas  * Prepend a header in front of the collected stuff
3771244Skas  * and return the new file.
3781244Skas  */
3791244Skas 
3801244Skas FILE *
3811244Skas infix(hp, fi)
3821244Skas 	struct header *hp;
3831244Skas 	FILE *fi;
3841244Skas {
3851244Skas 	extern char tempMail[];
3861244Skas 	register FILE *nfo, *nfi;
3871244Skas 	register int c;
3881244Skas 
3892062Skas 	rewind(fi);
3901244Skas 	if ((nfo = fopen(tempMail, "w")) == NULL) {
3911244Skas 		perror(tempMail);
3921244Skas 		return(fi);
3931244Skas 	}
3941244Skas 	if ((nfi = fopen(tempMail, "r")) == NULL) {
3951244Skas 		perror(tempMail);
3961244Skas 		fclose(nfo);
3971244Skas 		return(fi);
3981244Skas 	}
3991244Skas 	remove(tempMail);
4001244Skas 	puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
4011244Skas 	c = getc(fi);
4021244Skas 	while (c != EOF) {
4031244Skas 		putc(c, nfo);
4041244Skas 		c = getc(fi);
4051244Skas 	}
4061244Skas 	if (ferror(fi)) {
4071244Skas 		perror("read");
4081244Skas 		return(fi);
4091244Skas 	}
4101244Skas 	fflush(nfo);
4111244Skas 	if (ferror(nfo)) {
4121244Skas 		perror(tempMail);
4131244Skas 		fclose(nfo);
4141244Skas 		fclose(nfi);
4151244Skas 		return(fi);
4161244Skas 	}
4171244Skas 	fclose(nfo);
4181244Skas 	fclose(fi);
4191244Skas 	rewind(nfi);
4201244Skas 	return(nfi);
4211244Skas }
4221244Skas 
4231244Skas /*
4241244Skas  * Dump the to, subject, cc header on the
4251244Skas  * passed file buffer.
4261244Skas  */
4271244Skas 
4281244Skas puthead(hp, fo, w)
4291244Skas 	struct header *hp;
4301244Skas 	FILE *fo;
4311244Skas {
4321244Skas 	register int gotcha;
4331244Skas 
4341244Skas 	gotcha = 0;
4351244Skas 	if (hp->h_to != NOSTR && w & GTO)
4361244Skas 		fprintf(fo, "To: "), fmt(hp->h_to, fo), gotcha++;
4371244Skas 	if (hp->h_subject != NOSTR && w & GSUBJECT)
4381244Skas 		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
4391244Skas 	if (hp->h_cc != NOSTR && w & GCC)
4401244Skas 		fprintf(fo, "Cc: "), fmt(hp->h_cc, fo), gotcha++;
4411244Skas 	if (hp->h_bcc != NOSTR && w & GBCC)
4421244Skas 		fprintf(fo, "Bcc: "), fmt(hp->h_bcc, fo), gotcha++;
4431244Skas 	if (gotcha && w & GNL)
4441244Skas 		putc('\n', fo);
4451244Skas 	return(0);
4461244Skas }
4471244Skas 
4481244Skas /*
4491244Skas  * Format the given text to not exceed 72 characters.
4501244Skas  */
4511244Skas 
4521244Skas fmt(str, fo)
4531244Skas 	register char *str;
4541244Skas 	register FILE *fo;
4551244Skas {
4561244Skas 	register int col;
4571244Skas 	register char *cp;
4581244Skas 
4591244Skas 	cp = str;
4601244Skas 	col = 0;
4611244Skas 	while (*cp) {
4621244Skas 		if (*cp == ' ' && col > 65) {
4631244Skas 			fprintf(fo, "\n    ");
4641244Skas 			col = 4;
4651244Skas 			cp++;
4661244Skas 			continue;
4671244Skas 		}
4681244Skas 		putc(*cp++, fo);
4691244Skas 		col++;
4701244Skas 	}
4711244Skas 	putc('\n', fo);
4721244Skas }
4731244Skas 
4741244Skas /*
4751244Skas  * Save the outgoing mail on the passed file.
4761244Skas  */
4771244Skas 
4781244Skas savemail(name, hp, fi)
4791244Skas 	char name[];
4801244Skas 	struct header *hp;
4811244Skas 	FILE *fi;
4821244Skas {
4831244Skas 	register FILE *fo;
4841244Skas 	register int c;
4851244Skas 	long now;
4861244Skas 	char *n;
4871244Skas 
4881244Skas 	if ((fo = fopen(name, "a")) == NULL) {
4891244Skas 		perror(name);
4901244Skas 		return(-1);
4911244Skas 	}
4921244Skas 	time(&now);
4931244Skas 	n = rflag;
4941244Skas 	if (n == NOSTR)
4951244Skas 		n = myname;
4961244Skas 	fprintf(fo, "From %s %s", n, ctime(&now));
4971244Skas 	rewind(fi);
4981244Skas 	for (c = getc(fi); c != EOF; c = getc(fi))
4991244Skas 		putc(c, fo);
5001244Skas 	fprintf(fo, "\n");
5011244Skas 	fflush(fo);
5021244Skas 	if (ferror(fo))
5031244Skas 		perror(name);
5041244Skas 	fclose(fo);
5051244Skas 	return(0);
5061244Skas }
507