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