xref: /illumos-gate/usr/src/cmd/mail/sendmail.c (revision c65ebfc7045424bd04a6c7719a27b0ad3399ad54)
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  */
34 void
35 sendmail(argc, argv)
36 char **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 
390 wrapsend:
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