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