xref: /illumos-gate/usr/src/cmd/mail/sendmail.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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  */
37 void
38 sendmail(argc, argv)
39 char **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 
393 wrapsend:
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