xref: /original-bsd/usr.bin/mail/send.c (revision 6c57d260)
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	1.5 04/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;
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 		execv(DELIVERMAIL, namelist);
330 #endif DELIVERMAIL
331 		execv(MAIL, namelist);
332 		perror(MAIL);
333 		exit(1);
334 	}
335 
336 out:
337 	if (remote) {
338 		while ((p = wait(&s)) != pid && p != -1)
339 			;
340 		if (s != 0)
341 			senderr++;
342 		pid = 0;
343 	}
344 	fclose(mtf);
345 	return(pid);
346 }
347 
348 /*
349  * Fix the header by glopping all of the expanded names from
350  * the distribution list into the appropriate fields.
351  * If there are any ARPA net recipients in the message,
352  * we must insert commas, alas.
353  */
354 
355 fixhead(hp, tolist)
356 	struct header *hp;
357 	struct name *tolist;
358 {
359 	register struct name *nlist;
360 	register int f;
361 	register struct name *np;
362 
363 	for (f = 0, np = tolist; np != NIL; np = np->n_flink)
364 		if (any('@', np->n_name)) {
365 			f |= GCOMMA;
366 			break;
367 		}
368 
369 	if (debug && f & GCOMMA)
370 		fprintf(stderr, "Should be inserting commas in recip lists\n");
371 	hp->h_to = detract(tolist, GTO|f);
372 	hp->h_cc = detract(tolist, GCC|f);
373 }
374 
375 /*
376  * Prepend a header in front of the collected stuff
377  * and return the new file.
378  */
379 
380 FILE *
381 infix(hp, fi)
382 	struct header *hp;
383 	FILE *fi;
384 {
385 	extern char tempMail[];
386 	register FILE *nfo, *nfi;
387 	register int c;
388 
389 	rewind(fi);
390 	if ((nfo = fopen(tempMail, "w")) == NULL) {
391 		perror(tempMail);
392 		return(fi);
393 	}
394 	if ((nfi = fopen(tempMail, "r")) == NULL) {
395 		perror(tempMail);
396 		fclose(nfo);
397 		return(fi);
398 	}
399 	remove(tempMail);
400 	puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
401 	c = getc(fi);
402 	while (c != EOF) {
403 		putc(c, nfo);
404 		c = getc(fi);
405 	}
406 	if (ferror(fi)) {
407 		perror("read");
408 		return(fi);
409 	}
410 	fflush(nfo);
411 	if (ferror(nfo)) {
412 		perror(tempMail);
413 		fclose(nfo);
414 		fclose(nfi);
415 		return(fi);
416 	}
417 	fclose(nfo);
418 	fclose(fi);
419 	rewind(nfi);
420 	return(nfi);
421 }
422 
423 /*
424  * Dump the to, subject, cc header on the
425  * passed file buffer.
426  */
427 
428 puthead(hp, fo, w)
429 	struct header *hp;
430 	FILE *fo;
431 {
432 	register int gotcha;
433 
434 	gotcha = 0;
435 	if (hp->h_to != NOSTR && w & GTO)
436 		fprintf(fo, "To: "), fmt(hp->h_to, fo), gotcha++;
437 	if (hp->h_subject != NOSTR && w & GSUBJECT)
438 		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
439 	if (hp->h_cc != NOSTR && w & GCC)
440 		fprintf(fo, "Cc: "), fmt(hp->h_cc, fo), gotcha++;
441 	if (hp->h_bcc != NOSTR && w & GBCC)
442 		fprintf(fo, "Bcc: "), fmt(hp->h_bcc, fo), gotcha++;
443 	if (gotcha && w & GNL)
444 		putc('\n', fo);
445 	return(0);
446 }
447 
448 /*
449  * Format the given text to not exceed 72 characters.
450  */
451 
452 fmt(str, fo)
453 	register char *str;
454 	register FILE *fo;
455 {
456 	register int col;
457 	register char *cp;
458 
459 	cp = str;
460 	col = 0;
461 	while (*cp) {
462 		if (*cp == ' ' && col > 65) {
463 			fprintf(fo, "\n    ");
464 			col = 4;
465 			cp++;
466 			continue;
467 		}
468 		putc(*cp++, fo);
469 		col++;
470 	}
471 	putc('\n', fo);
472 }
473 
474 /*
475  * Save the outgoing mail on the passed file.
476  */
477 
478 savemail(name, hp, fi)
479 	char name[];
480 	struct header *hp;
481 	FILE *fi;
482 {
483 	register FILE *fo;
484 	register int c;
485 	long now;
486 	char *n;
487 
488 	if ((fo = fopen(name, "a")) == NULL) {
489 		perror(name);
490 		return(-1);
491 	}
492 	time(&now);
493 	n = rflag;
494 	if (n == NOSTR)
495 		n = myname;
496 	fprintf(fo, "From %s %s", n, ctime(&now));
497 	rewind(fi);
498 	for (c = getc(fi); c != EOF; c = getc(fi))
499 		putc(c, fo);
500 	fprintf(fo, "\n");
501 	fflush(fo);
502 	if (ferror(fo))
503 		perror(name);
504 	fclose(fo);
505 	return(0);
506 }
507