sendmail.c revision 8e4728571e959f3afb368407b22125278412c9d4
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22/*	  All Rights Reserved  	*/
23
24
25/*
26 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
27 * Use is subject to license terms.
28 */
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include "mail.h"
33#include <sys/param.h>
34/*
35 * Send mail - High level sending routine
36 */
37void
38sendmail(argc, argv)
39char **argv;
40{
41	char		**args;
42	char		*tp, *zp;
43	char		buf[2048], last1c;
44	FILE		*input;
45	struct stat64 	sbuf;
46	int		aret;
47	int		i, n;
48	int		oldn = 1;
49	int		ttyf = 0;
50	int		pushrest = 0;
51	int		hdrtyp = 0;
52	int		ctf = FALSE;
53	int		binflg = 0;
54	long		count = 0L;
55	struct tm	*bp;
56	struct hdrs	*hptr;
57	static char	pn[] = "sendmail";
58	reciplist	list;
59
60	Dout(pn, 0, "entered\n");
61	new_reciplist(&list);
62	for (i = 1; i < argc; ++i) {
63		if (argv[i][0] == '-') {
64			if (argv[i][1] == '\0') {
65				errmsg(E_SYNTAX,
66				    "Hyphens MAY NOT be followed by spaces");
67			}
68			if (i > 1) {
69				errmsg(E_SYNTAX,
70				    "Options MUST PRECEDE persons");
71			}
72			done(0);
73		}
74		/*
75		 *	Ensure no NULL names in list
76		 */
77		if (argv[i][0] == '\0' || argv[i][strlen(argv[i])-1] == '!') {
78			errmsg(E_SYNTAX, "Null names are not allowed");
79			done(0);
80		}
81		/* Don't check for duplication */
82		add_recip(&list, argv[i], FALSE);
83	}
84
85	mktmp();
86	/*
87	 *	Format time
88	 */
89	time(&iop);
90	bp = localtime(&iop);
91	tp = asctime(bp);
92	zp = tzname[bp->tm_isdst];
93	sprintf(datestring, "%.16s %.3s %.5s", tp, zp, tp+20);
94	trimnl(datestring);
95	/* asctime: Fri Sep 30 00:00:00 1986\n */
96	/* 	0123456789012345678901234  */
97	/* RFCtime: Fri, 28 Jul 89 10:30 EDT   */
98	sprintf(RFC822datestring, "%.3s, %.2s %.3s %.4s %.5s %.3s",
99		tp, tp+8, tp+4, tp+20, tp+11, zp);
100
101	/*
102	 * Write out the from line header for the letter
103	 */
104	if (fromflag && deliverflag && from_user[0] != '\0') {
105		(void) snprintf(buf, sizeof (buf), "%s%s %s\n",
106			header[H_FROM].tag, from_user, datestring);
107	} else {
108		(void) snprintf(buf, sizeof (buf), "%s%s %s\n",
109			header[H_FROM].tag, my_name, datestring);
110	}
111	if (!wtmpf(buf, strlen(buf))) {
112		done(0);
113	}
114	savehdrs(buf, H_FROM);
115
116	/*
117	 * Copy to list in mail entry?
118	 */
119	if (flgt == 1 && argc > 1) {
120		aret = argc;
121		args = argv;
122		while (--aret > 0) {
123			(void) snprintf(buf, sizeof (buf),
124			    "%s %s\n", header[H_TO].tag, *++args);
125			if (!wtmpf(buf, strlen(buf))) {
126				done(0);
127			}
128			savehdrs(buf, H_TO);
129		}
130	}
131
132	flgf = 1;	/* reset when first read of message body succeeds */
133	/*
134	 * Read mail message, allowing for lines of infinite
135	 * length. This is tricky, have to watch for newlines.
136	 */
137	saveint = setsig(SIGINT, savdead);
138	last1c = ' ';	/* anything other than newline */
139	ttyf = isatty(fileno(stdin));
140	pushrest = 0;
141
142	/*
143	 * scan header & save relevant info.
144	 */
145	(void) strlcpy(fromU, my_name, sizeof (fromU));
146	fromS[0] = 0;	/* set up for >From scan */
147	input = stdin;
148	/*
149	 * Fifofs cannot handle if the inode number crosses
150	 * 32-bit limit. This results in overflow, if the
151	 * input steam is a pipe. Using 64-bit interface to
152	 * take care of that.
153	 */
154	if (fstat64(fileno(input), &sbuf) == 0) {
155		/* Also care if we could not handle large mail. */
156		if ((sbuf.st_size > MAXOFF_T) || (sbuf.st_blocks > LONG_MAX)) {
157			fprintf(stderr, "%s: stdin: %s\n", program,
158			    strerror(EOVERFLOW));
159			exit(1);
160		}
161	}
162
163	while ((n = getline(line, sizeof (line), stdin)) > 0) {
164		last1c = line[n-1];
165		if (pushrest) {
166			if (!wtmpf(line, n)) {
167				done(0);
168			}
169			pushrest = (last1c != '\n');
170			continue;
171		}
172		pushrest = (last1c != '\n');
173
174		if ((hdrtyp = isheader(line, &ctf)) == FALSE) {
175			break;
176		}
177		flgf = 0;
178		switch (hdrtyp) {
179		case H_RVERS:
180			/* Are we dealing with a delivery report? */
181			/* dflag = 9 ==> do not return on failure */
182			dflag = 9;
183			Dout(pn, 0, "dflag = 9\n");
184			break;
185		case H_FROM:
186			if (!wtmpf(">", 1)) {
187				done(0);
188			}
189			/* note dropthru */
190			hdrtyp = H_FROM1;
191		case H_FROM1:
192			if (substr(line, "forwarded by") > -1) {
193				break;
194			}
195			pickFrom(line);
196			if (Rpath[0] != '\0') {
197				strcat(Rpath, "!");
198			}
199			(void) strlcat(Rpath, fromS, sizeof (Rpath));
200			n = 0; /* don't copy remote from's into mesg. */
201			break;
202		case H_MIMEVERS:
203		case H_CLEN:
204		case H_CTYPE:
205			/* suppress it: only generated if needed */
206			n = 0; /* suppress */
207			break;
208		case H_TCOPY:
209			/* Write out placeholder for later */
210			(void) snprintf(buf, sizeof (buf), "%s \n",
211			    header[H_TCOPY].tag);
212			if (!wtmpf(buf, strlen(buf))) {
213				done(0);
214			}
215			n = 0; /* suppress */
216			break;
217		case H_MTYPE:
218			if (flgm) {
219				/* suppress if message-type argument */
220				n = 0;
221			}
222			break;
223		case H_CONT:
224			if (oldn == 0) {
225				/* suppress continuation line also */
226				n = 0;
227			}
228			break;
229		}
230		oldn = n;	/* remember if this line was suppressed */
231		if (n && !wtmpf(line, n)) {
232			done(0);
233		}
234		if (!n) savehdrs(line, hdrtyp);
235	}
236	if (Rpath[0] != '\0') {
237		strcat(Rpath, "!");
238	}
239	(void) strlcat(Rpath, fromU, sizeof (Rpath));
240
241	/* push out message type if so requested */
242	if (flgm) {	/* message-type */
243		snprintf(buf, sizeof (buf), "%s%s\n",
244		    header[H_MTYPE].tag, msgtype);
245		if (!wtmpf(buf, strlen(buf))) {
246			done(0);
247		}
248	}
249
250	memcpy(buf, line, n);
251	if (n == 0 || (ttyf && !strncmp(buf, ".\n", 2))) {
252		if (flgf) {
253			/* no input */
254			return;
255		} else {
256			/*
257			 * no content: put mime-version, content-type
258			 * and -length only if explicitly present.
259			 * Write out 'place-holders' only. (see below....)
260			 */
261			if ((hptr = hdrlines[H_MIMEVERS].head) !=
262						    (struct hdrs *)NULL) {
263				(void) snprintf(line, sizeof (line), "%s \n",
264				    header[H_MIMEVERS].tag);
265				if (!wtmpf(line, strlen(line))) {
266					done(0);
267				}
268			}
269			if ((hptr = hdrlines[H_CTYPE].head) !=
270						    (struct hdrs *)NULL) {
271				(void) snprintf(line, sizeof (line), "%s \n",
272				    header[H_CTYPE].tag);
273				if (!wtmpf(line, strlen(line))) {
274					done(0);
275				}
276			}
277			if ((hptr = hdrlines[H_CLEN].head) !=
278						    (struct hdrs *)NULL) {
279				(void) snprintf(line, sizeof (line), "%s \n",
280				    header[H_CLEN].tag);
281				if (!wtmpf(line, strlen(line))) {
282					done(0);
283				}
284			}
285			goto wrapsend;
286		}
287	}
288
289	if (n == 1 && last1c == '\n') {	/* blank line -- suppress */
290		n = getline(buf, sizeof (buf), stdin);
291		if (n == 0 || (ttyf && !strncmp(buf, ".\n", 2))) {
292			/*
293			 * no content: put mime-version, content-type
294			 * and -length only if explicitly present.
295			 * Write out 'place-holders' only. (see below....)
296			 */
297			if ((hptr = hdrlines[H_MIMEVERS].head) !=
298						    (struct hdrs *)NULL) {
299				(void) snprintf(line, sizeof (line), "%s \n",
300				    header[H_MIMEVERS].tag);
301				if (!wtmpf(line, strlen(line))) {
302					done(0);
303				}
304			}
305			if ((hptr = hdrlines[H_CTYPE].head) !=
306						    (struct hdrs *)NULL) {
307				(void) snprintf(line, sizeof (line), "%s \n",
308				    header[H_CTYPE].tag);
309				if (!wtmpf(line, strlen(line))) {
310					done(0);
311				}
312			}
313			if ((hptr = hdrlines[H_CLEN].head) !=
314						    (struct hdrs *)NULL) {
315				(void) snprintf(line, sizeof (line), "%s \n",
316				    header[H_CLEN].tag);
317				if (!wtmpf(line, strlen(line))) {
318					done(0);
319				}
320			}
321			goto wrapsend;
322		}
323	}
324
325	if (debug > 0) {
326		buf[n] = '\0';
327		Dout(pn, 0, "header scan complete, readahead %d = \"%s\"\n",
328		    n, buf);
329	}
330
331	/*
332	 * Write out H_MIMEVERS, H_CTYPE & H_CLEN lines. These are used only as
333	 * placeholders in the tmp file. When the 'real' message is sent,
334	 * the proper values will be put out by copylet().
335	 */
336	(void) snprintf(line, sizeof (line), "%s \n", header[H_MIMEVERS].tag);
337	if (!wtmpf(line, strlen(line))) {
338		done(0);
339	}
340	if (hdrlines[H_MIMEVERS].head == (struct hdrs *)NULL) {
341		savehdrs(line, H_MIMEVERS);
342	}
343	(void) snprintf(line, sizeof (line), "%s \n", header[H_CTYPE].tag);
344	if (!wtmpf(line, strlen(line))) {
345		done(0);
346	}
347	if (hdrlines[H_CTYPE].head == (struct hdrs *)NULL) {
348		savehdrs(line, H_CTYPE);
349	}
350	(void) snprintf(line, sizeof (line), "%s \n", header[H_CLEN].tag);
351	if (!wtmpf(line, strlen(line))) {
352		done(0);
353	}
354	if (hdrlines[H_CLEN].head == (struct hdrs *)NULL) {
355		savehdrs(line, H_CLEN);
356	}
357	/* and a blank line */
358	if (!wtmpf("\n", 1)) {
359		done(0);
360	}
361	Dout(pn, 0, "header out completed\n");
362
363	pushrest = 0;
364	count = 0L;
365	/*
366	 *	Are we returning mail from a delivery failure of an old-style
367	 *	(SVR3.1 or SVR3.0) rmail? If so, we won't return THIS on failure
368	 *	[This line should occur as the FIRST non-blank non-header line]
369	 */
370	if (!strncmp("***** UNDELIVERABLE MAIL sent to", buf, 32)) {
371		dflag = 9; /* 9 says do not return on failure */
372		Dout(pn, 0, "found old-style UNDELIVERABLE line. dflag = 9\n");
373	}
374
375	/* scan body of message */
376	while (n > 0) {
377		if (ttyf && !strcmp(buf, ".\n"))
378			break;
379		if (!binflg) {
380			binflg = !istext((unsigned char *)buf, n);
381		}
382
383		if (!wtmpf(buf, n)) {
384			done(0);
385		}
386		count += n;
387		n = ttyf
388			? getline(buf, sizeof (buf), stdin)
389			: fread(buf, 1, sizeof (buf), stdin);
390	}
391	setsig(SIGINT, saveint);
392
393wrapsend:
394	/*
395	 *	In order to use some of the subroutines that are used to
396	 *	read mail, the let array must be set up
397	 */
398	nlet = 1;
399	let[0].adr = 0;
400	let[1].adr = ftell(tmpf);
401	let[0].text = (binflg == 1 ? FALSE : TRUE);
402	Dout(pn, 0, "body copy complete, count %ld\n", count);
403	/*
404	 * Modify value of H_MIMEVERS if necessary.
405	 */
406	if ((hptr = hdrlines[H_MIMEVERS].head) != (struct hdrs *)NULL) {
407		if (strlen(hptr->value) == 0) {
408			(void) strlcpy(hptr->value, "1.0",
409			    sizeof (hptr->value));
410		}
411	}
412	/*
413	 * Modify value of H_CTYPE if necessary.
414	 */
415	if ((hptr = hdrlines[H_CTYPE].head) != (struct hdrs *)NULL) {
416		if (strlen(hptr->value) == 0) {
417			(void) strlcpy(hptr->value, "text/plain",
418			    sizeof (hptr->value));
419		}
420	}
421	/*
422	 * Set 'place-holder' value of content length to true value
423	 */
424	if ((hptr = hdrlines[H_CLEN].head) != (struct hdrs *)NULL) {
425		(void) snprintf(hptr->value, sizeof (hptr->value),
426		    "%ld", count);
427	}
428
429	if (fclose(tmpf) == EOF) {
430		tmperr();
431		done(0);
432	}
433
434	tmpf = doopen(lettmp, "r+", E_TMP);
435
436	/* Do not send mail on SIGINT */
437	if (dflag == 2) {
438		done(0);
439	}
440
441	sendlist(&list, 0, 0);
442	done(0);
443}
444