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