xref: /original-bsd/usr.bin/mail/send.c (revision c43e4352)
1 #
2 
3 #include "rcv.h"
4 #ifdef VMUNIX
5 #include <sys/wait.h>
6 #endif
7 #include <ctype.h>
8 #include <sys/stat.h>
9 
10 /*
11  * Mail -- a mail program
12  *
13  * Mail to others.
14  */
15 
16 static char *SccsId = "@(#)send.c	2.12 07/02/83";
17 
18 /*
19  * Send message described by the passed pointer to the
20  * passed output buffer.  Return -1 on error, but normally
21  * the number of lines written.  Adjust the status: field
22  * if need be.  If doign is set, suppress ignored header fields.
23  */
24 send(mailp, obuf, doign)
25 	struct message *mailp;
26 	FILE *obuf;
27 {
28 	register struct message *mp;
29 	register int t;
30 	long c;
31 	FILE *ibuf;
32 	char line[LINESIZE], field[BUFSIZ];
33 	int lc, ishead, infld, fline, dostat;
34 	char *cp, *cp2;
35 
36 	mp = mailp;
37 	ibuf = setinput(mp);
38 	c = mp->m_size;
39 	ishead = 1;
40 	dostat = 1;
41 	infld = 0;
42 	fline = 1;
43 	lc = 0;
44 	while (c > 0L) {
45 		fgets(line, LINESIZE, ibuf);
46 		c -= (long) strlen(line);
47 		lc++;
48 		if (ishead) {
49 			/*
50 			 * First line is the From line, so no headers
51 			 * there to worry about
52 			 */
53 			if (fline) {
54 				fline = 0;
55 				goto writeit;
56 			}
57 			/*
58 			 * If line is blank, we've reached end of
59 			 * headers, so force out status: field
60 			 * and note that we are no longer in header
61 			 * fields
62 			 */
63 			if (line[0] == '\n') {
64 				if (dostat) {
65 					statusput(mailp, obuf, doign);
66 					dostat = 0;
67 				}
68 				ishead = 0;
69 				goto writeit;
70 			}
71 			/*
72 			 * If this line is a continuation (via space or tab)
73 			 * of a previous header field, just echo it
74 			 * (unless the field should be ignored).
75 			 */
76 			if (infld && (isspace(line[0]) || line[0] == '\t')) {
77 				if (doign && isign(field)) continue;
78 				goto writeit;
79 			}
80 			infld = 0;
81 			/*
82 			 * If we are no longer looking at real
83 			 * header lines, force out status:
84 			 * This happens in uucp style mail where
85 			 * there are no headers at all.
86 			 */
87 			if (!headerp(line)) {
88 				if (dostat) {
89 					statusput(mailp, obuf, doign);
90 					dostat = 0;
91 				}
92 				putc('\n', obuf);
93 				ishead = 0;
94 				goto writeit;
95 			}
96 			infld++;
97 			/*
98 			 * Pick up the header field.
99 			 * If it is an ignored field and
100 			 * we care about such things, skip it.
101 			 */
102 			cp = line;
103 			cp2 = field;
104 			while (*cp && *cp != ':' && !isspace(*cp))
105 				*cp2++ = *cp++;
106 			*cp2 = 0;
107 			if (doign && isign(field))
108 				continue;
109 			/*
110 			 * If the field is "status," go compute and print the
111 			 * real Status: field
112 			 */
113 			if (icequal(field, "status")) {
114 				if (dostat) {
115 					statusput(mailp, obuf, doign);
116 					dostat = 0;
117 				}
118 				continue;
119 			}
120 		}
121 writeit:
122 		fputs(line, obuf);
123 		if (ferror(obuf))
124 			return(-1);
125 	}
126 	if (ferror(obuf))
127 		return(-1);
128 	if (ishead && (mailp->m_flag & MSTATUS))
129 		printf("failed to fix up status field\n");
130 	return(lc);
131 }
132 
133 /*
134  * Test if the passed line is a header line, RFC 733 style.
135  */
136 headerp(line)
137 	register char *line;
138 {
139 	register char *cp = line;
140 
141 	while (*cp && !isspace(*cp) && *cp != ':')
142 		cp++;
143 	while (*cp && isspace(*cp))
144 		cp++;
145 	return(*cp == ':');
146 }
147 
148 /*
149  * Output a reasonable looking status field.
150  * But if "status" is ignored and doign, forget it.
151  */
152 statusput(mp, obuf, doign)
153 	register struct message *mp;
154 	register FILE *obuf;
155 {
156 	char statout[3];
157 
158 	if (doign && isign("status"))
159 		return;
160 	if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
161 		return;
162 	if (mp->m_flag & MREAD)
163 		strcpy(statout, "R");
164 	else
165 		strcpy(statout, "");
166 	if ((mp->m_flag & MNEW) == 0)
167 		strcat(statout, "O");
168 	fprintf(obuf, "Status: %s\n", statout);
169 }
170 
171 
172 /*
173  * Interface between the argument list and the mail1 routine
174  * which does all the dirty work.
175  */
176 
177 mail(people)
178 	char **people;
179 {
180 	register char *cp2;
181 	register int s;
182 	char *buf, **ap;
183 	struct header head;
184 
185 	for (s = 0, ap = people; *ap != (char *) -1; ap++)
186 		s += strlen(*ap) + 1;
187 	buf = salloc(s+1);
188 	cp2 = buf;
189 	for (ap = people; *ap != (char *) -1; ap++) {
190 		cp2 = copy(*ap, cp2);
191 		*cp2++ = ' ';
192 	}
193 	if (cp2 != buf)
194 		cp2--;
195 	*cp2 = '\0';
196 	head.h_to = buf;
197 	head.h_subject = NOSTR;
198 	head.h_cc = NOSTR;
199 	head.h_bcc = NOSTR;
200 	head.h_seq = 0;
201 	mail1(&head);
202 	return(0);
203 }
204 
205 
206 /*
207  * Send mail to a bunch of user names.  The interface is through
208  * the mail routine below.
209  */
210 
211 sendmail(str)
212 	char *str;
213 {
214 	register char **ap;
215 	char *bufp;
216 	register int t;
217 	struct header head;
218 
219 	if (blankline(str))
220 		head.h_to = NOSTR;
221 	else
222 		head.h_to = str;
223 	head.h_subject = NOSTR;
224 	head.h_cc = NOSTR;
225 	head.h_bcc = NOSTR;
226 	head.h_seq = 0;
227 	mail1(&head);
228 	return(0);
229 }
230 
231 /*
232  * Mail a message on standard input to the people indicated
233  * in the passed header.  (Internal interface).
234  */
235 
236 mail1(hp)
237 	struct header *hp;
238 {
239 	register char *cp;
240 	int pid, i, s, p, gotcha;
241 	char **namelist, *deliver;
242 	struct name *to, *np;
243 	struct stat sbuf;
244 	FILE *mtf, *postage;
245 	int remote = rflag != NOSTR || rmail;
246 	char **t;
247 
248 	/*
249 	 * Collect user's mail from standard input.
250 	 * Get the result as mtf.
251 	 */
252 
253 	pid = -1;
254 	if ((mtf = collect(hp)) == NULL)
255 		return(-1);
256 	hp->h_seq = 1;
257 	if (hp->h_subject == NOSTR)
258 		hp->h_subject = sflag;
259 	if (intty && value("askcc") != NOSTR)
260 		grabh(hp, GCC);
261 	else if (intty) {
262 		printf("EOT\n");
263 		flush();
264 	}
265 
266 	/*
267 	 * Now, take the user names from the combined
268 	 * to and cc lists and do all the alias
269 	 * processing.
270 	 */
271 
272 	senderr = 0;
273 	to = usermap(cat(extract(hp->h_bcc, GBCC),
274 	    cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
275 	if (to == NIL) {
276 		printf("No recipients specified\n");
277 		goto topdog;
278 	}
279 
280 	/*
281 	 * Look through the recipient list for names with /'s
282 	 * in them which we write to as files directly.
283 	 */
284 
285 	to = outof(to, mtf, hp);
286 	rewind(mtf);
287 	to = verify(to);
288 	if (senderr && !remote) {
289 topdog:
290 
291 		if (fsize(mtf) != 0) {
292 			remove(deadletter);
293 			exwrite(deadletter, mtf, 1);
294 			rewind(mtf);
295 		}
296 	}
297 	for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
298 		if ((np->n_type & GDEL) == 0) {
299 			gotcha++;
300 			break;
301 		}
302 	if (!gotcha)
303 		goto out;
304 	to = elide(to);
305 	mechk(to);
306 	if (count(to) > 1)
307 		hp->h_seq++;
308 	if (hp->h_seq > 0 && !remote) {
309 		fixhead(hp, to);
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 		if ((mtf = infix(hp, mtf)) == NULL) {
316 			fprintf(stderr, ". . . message lost, sorry.\n");
317 			return(-1);
318 		}
319 	}
320 	namelist = unpack(to);
321 	if (debug) {
322 		printf("Recipients of message:\n");
323 		for (t = namelist; *t != NOSTR; t++)
324 			printf(" \"%s\"", *t);
325 		printf("\n");
326 		fflush(stdout);
327 		return;
328 	}
329 	if ((cp = value("record")) != NOSTR)
330 		savemail(expand(cp), hp, mtf);
331 
332 	/*
333 	 * Wait, to absorb a potential zombie, then
334 	 * fork, set up the temporary mail file as standard
335 	 * input for "mail" and exec with the user list we generated
336 	 * far above. Return the process id to caller in case he
337 	 * wants to await the completion of mail.
338 	 */
339 
340 #ifdef VMUNIX
341 #ifdef	pdp11
342 	while (wait2(&s, WNOHANG) > 0)
343 #endif
344 #if defined(vax) || defined(sun)
345 	while (wait3(&s, WNOHANG, 0) > 0)
346 #endif
347 		;
348 #else
349 	wait(&s);
350 #endif
351 	rewind(mtf);
352 	pid = fork();
353 	if (pid == -1) {
354 		perror("fork");
355 		remove(deadletter);
356 		exwrite(deadletter, mtf, 1);
357 		goto out;
358 	}
359 	if (pid == 0) {
360 		sigchild();
361 #ifdef SIGTSTP
362 		if (remote == 0) {
363 			sigset(SIGTSTP, SIG_IGN);
364 			sigset(SIGTTIN, SIG_IGN);
365 			sigset(SIGTTOU, SIG_IGN);
366 		}
367 #endif
368 		for (i = SIGHUP; i <= SIGQUIT; i++)
369 			sigset(i, SIG_IGN);
370 		if (!stat(POSTAGE, &sbuf))
371 			if ((postage = fopen(POSTAGE, "a")) != NULL) {
372 				fprintf(postage, "%s %d %d\n", myname,
373 				    count(to), fsize(mtf));
374 				fclose(postage);
375 			}
376 		s = fileno(mtf);
377 		for (i = 3; i < 15; i++)
378 			if (i != s)
379 				close(i);
380 		close(0);
381 		dup(s);
382 		close(s);
383 #ifdef CC
384 		submit(getpid());
385 #endif CC
386 #ifdef SENDMAIL
387 		if ((deliver = value("sendmail")) == NOSTR)
388 			deliver = SENDMAIL;
389 		execv(deliver, namelist);
390 #endif SENDMAIL
391 		execv(MAIL, namelist);
392 		perror(MAIL);
393 		exit(1);
394 	}
395 
396 out:
397 	if (remote || (value("verbose") != NOSTR)) {
398 		while ((p = wait(&s)) != pid && p != -1)
399 			;
400 		if (s != 0)
401 			senderr++;
402 		pid = 0;
403 	}
404 	fclose(mtf);
405 	return(pid);
406 }
407 
408 /*
409  * Fix the header by glopping all of the expanded names from
410  * the distribution list into the appropriate fields.
411  * If there are any ARPA net recipients in the message,
412  * we must insert commas, alas.
413  */
414 
415 fixhead(hp, tolist)
416 	struct header *hp;
417 	struct name *tolist;
418 {
419 	register struct name *nlist;
420 	register int f;
421 	register struct name *np;
422 
423 	for (f = 0, np = tolist; np != NIL; np = np->n_flink)
424 		if (any('@', np->n_name)) {
425 			f |= GCOMMA;
426 			break;
427 		}
428 
429 	if (debug && f & GCOMMA)
430 		fprintf(stderr, "Should be inserting commas in recip lists\n");
431 	hp->h_to = detract(tolist, GTO|f);
432 	hp->h_cc = detract(tolist, GCC|f);
433 }
434 
435 /*
436  * Prepend a header in front of the collected stuff
437  * and return the new file.
438  */
439 
440 FILE *
441 infix(hp, fi)
442 	struct header *hp;
443 	FILE *fi;
444 {
445 	extern char tempMail[];
446 	register FILE *nfo, *nfi;
447 	register int c;
448 
449 	rewind(fi);
450 	if ((nfo = fopen(tempMail, "w")) == NULL) {
451 		perror(tempMail);
452 		return(fi);
453 	}
454 	if ((nfi = fopen(tempMail, "r")) == NULL) {
455 		perror(tempMail);
456 		fclose(nfo);
457 		return(fi);
458 	}
459 	remove(tempMail);
460 	puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
461 	c = getc(fi);
462 	while (c != EOF) {
463 		putc(c, nfo);
464 		c = getc(fi);
465 	}
466 	if (ferror(fi)) {
467 		perror("read");
468 		return(fi);
469 	}
470 	fflush(nfo);
471 	if (ferror(nfo)) {
472 		perror(tempMail);
473 		fclose(nfo);
474 		fclose(nfi);
475 		return(fi);
476 	}
477 	fclose(nfo);
478 	fclose(fi);
479 	rewind(nfi);
480 	return(nfi);
481 }
482 
483 /*
484  * Dump the to, subject, cc header on the
485  * passed file buffer.
486  */
487 
488 puthead(hp, fo, w)
489 	struct header *hp;
490 	FILE *fo;
491 {
492 	register int gotcha;
493 
494 	gotcha = 0;
495 	if (hp->h_to != NOSTR && w & GTO)
496 		fmt("To: ", hp->h_to, fo), gotcha++;
497 	if (hp->h_subject != NOSTR && w & GSUBJECT)
498 		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
499 	if (hp->h_cc != NOSTR && w & GCC)
500 		fmt("Cc: ", hp->h_cc, fo), gotcha++;
501 	if (hp->h_bcc != NOSTR && w & GBCC)
502 		fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
503 	if (gotcha && w & GNL)
504 		putc('\n', fo);
505 	return(0);
506 }
507 
508 /*
509  * Format the given text to not exceed 72 characters.
510  */
511 
512 fmt(str, txt, fo)
513 	register char *str, *txt;
514 	register FILE *fo;
515 {
516 	register int col;
517 	register char *bg, *bl, *pt, ch;
518 
519 	col = strlen(str);
520 	if (col)
521 		fprintf(fo, "%s", str);
522 	pt = bg = txt;
523 	bl = 0;
524 	while (*bg) {
525 		pt++;
526 		if (++col >72) {
527 			if (!bl) {
528 				bl = bg;
529 				while (*bl && !isspace(*bl))
530 					bl++;
531 			}
532 			if (!*bl)
533 				goto finish;
534 			ch = *bl;
535 			*bl = '\0';
536 			fprintf(fo, "%s\n    ", bg);
537 			col = 4;
538 			*bl = ch;
539 			pt = bg = ++bl;
540 			bl = 0;
541 		}
542 		if (!*pt) {
543 finish:
544 			fprintf(fo, "%s\n", bg);
545 			return;
546 		}
547 		if (isspace(*pt))
548 			bl = pt;
549 	}
550 }
551 
552 /*
553  * Save the outgoing mail on the passed file.
554  */
555 
556 savemail(name, hp, fi)
557 	char name[];
558 	struct header *hp;
559 	FILE *fi;
560 {
561 	register FILE *fo;
562 	register int c;
563 	long now;
564 	char *n;
565 
566 	if ((fo = fopen(name, "a")) == NULL) {
567 		perror(name);
568 		return(-1);
569 	}
570 	time(&now);
571 	n = rflag;
572 	if (n == NOSTR)
573 		n = myname;
574 	fprintf(fo, "From %s %s", n, ctime(&now));
575 	rewind(fi);
576 	for (c = getc(fi); c != EOF; c = getc(fi))
577 		putc(c, fo);
578 	fprintf(fo, "\n");
579 	fflush(fo);
580 	if (ferror(fo))
581 		perror(name);
582 	fclose(fo);
583 	return(0);
584 }
585