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