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