Home | History | Annotate | Download | only in mail
      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