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