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