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) 1985, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #include "rcv.h" 41 #include <locale.h> 42 43 /* 44 * mailx -- a modified version of a University of California at Berkeley 45 * mail program 46 * 47 * Mail to others. 48 */ 49 50 static void fmt(register char *str, register FILE *fo); 51 static FILE *infix(struct header *hp, FILE *fi); 52 static void statusput(register struct message *mp, register FILE *obuf, int doign, int (*fp)(const char *, FILE *)); 53 static int savemail(char name[], struct header *hp, FILE *fi); 54 static int sendmail(char *str); 55 static int Sendmail(char *str); 56 57 static off_t textpos; 58 59 /* 60 * Send message described by the passed pointer to the 61 * passed output buffer. Return -1 on error, but normally 62 * the number of lines written. Adjust the status: field 63 * if need be. If doign is set, suppress ignored header fields. 64 * Call (*fp)(line, obuf) to print the line. 65 */ 66 long 67 msend( 68 struct message *mailp, 69 FILE *obuf, 70 int flag, 71 int (*fp)(const char *, FILE *)) 72 { 73 register struct message *mp; 74 long clen, n, c; 75 FILE *ibuf; 76 char line[LINESIZE], field[BUFSIZ]; 77 int ishead, infld, fline, dostat, doclen, nread, unused; 78 char *cp, *cp2; 79 int doign = flag & M_IGNORE; 80 int oldign = 0; /* previous line was ignored */ 81 long lc; 82 83 mp = mailp; 84 if (mp->m_clen == 0) 85 setclen(mp); 86 ibuf = setinput(mp); 87 c = mp->m_size; 88 ishead = 1; 89 dostat = 1; 90 doclen = 1; 91 infld = 0; 92 fline = 1; 93 lc = 0; 94 clearerr(obuf); 95 while (c > 0L) { 96 nread = getaline(line, LINESIZE, ibuf, &unused); 97 c -= nread; 98 lc++; 99 if (ishead) { 100 /* 101 * First line is the From line, so no headers 102 * there to worry about 103 */ 104 if (fline) { 105 fline = 0; 106 goto writeit; 107 } 108 /* 109 * If line is blank, we've reached end of 110 * headers, so force out status: field 111 * and note that we are no longer in header 112 * fields. Also force out Content-Length: field. 113 */ 114 if (line[0] == '\n') { 115 if (dostat) { 116 statusput(mailp, obuf, doign, fp); 117 dostat = 0; 118 } 119 if (doclen && 120 !isign("content-length", flag&M_SAVING)) { 121 snprintf(field, sizeof (field), 122 "Content-Length: %ld\n", 123 mp->m_clen - 1); 124 (*fp)(field, obuf); 125 if (ferror(obuf)) 126 return(-1); 127 doclen = 0; 128 } 129 ishead = 0; 130 goto writeit; 131 } 132 /* 133 * If this line is a continuation 134 * of a previous header field, just echo it. 135 */ 136 if (isspace(line[0]) && infld) 137 if (oldign) 138 continue; 139 else 140 goto writeit; 141 infld = 0; 142 /* 143 * If we are no longer looking at real 144 * header lines, force out status: 145 * This happens in uucp style mail where 146 * there are no headers at all. 147 */ 148 if (!headerp(line)) { 149 if (dostat) { 150 statusput(mailp, obuf, doign, fp); 151 dostat = 0; 152 } 153 (*fp)("\n", obuf); 154 ishead = 0; 155 goto writeit; 156 } 157 infld++; 158 /* 159 * Pick up the header field. 160 * If it is an ignored field and 161 * we care about such things, skip it. 162 */ 163 cp = line; 164 cp2 = field; 165 while (*cp && *cp != ':' && !isspace(*cp)) 166 *cp2++ = *cp++; 167 *cp2 = 0; 168 oldign = doign && isign(field, flag&M_SAVING); 169 if (oldign) 170 continue; 171 /* 172 * If the field is "status," go compute and print the 173 * real Status: field 174 */ 175 if (icequal(field, "status")) { 176 if (dostat) { 177 statusput(mailp, obuf, doign, fp); 178 dostat = 0; 179 } 180 continue; 181 } 182 if (icequal(field, "content-length")) { 183 if (doclen) { 184 snprintf(line, sizeof (line), 185 "Content-Length: %ld\n", 186 mp->m_clen - 1); 187 (*fp)(line, obuf); 188 if (ferror(obuf)) 189 return(-1); 190 doclen = 0; 191 } 192 continue; 193 } 194 } 195 writeit: 196 if (!ishead && !mp->m_text && mp->m_clen != 0) { 197 if (line[0] == '\n') 198 putc('\n', obuf); 199 clen = mp->m_clen-1; 200 for (;;) { 201 n = clen < sizeof line ? clen : sizeof line; 202 if ((n = fread(line, 1, (int)n, ibuf)) <= 0) { 203 fprintf(stderr, gettext( 204 "\t(Unexpected end-of-file).\n")); 205 clen = 0; 206 } else { 207 if (fwrite(line, 1, (int)n, obuf) != n) { 208 fprintf(stderr, gettext( 209 "\tError writing to the new file.\n")); 210 fflush(obuf); 211 if (fferror(obuf)) 212 return (-1); 213 } 214 } 215 clen -= n; 216 if (clen <= 0) { 217 break; 218 } 219 } 220 c = 0L; 221 } else { 222 (*fp)(line, obuf); 223 if (ferror(obuf)) 224 return(-1); 225 } 226 } 227 fflush(obuf); 228 if (ferror(obuf)) 229 return(-1); 230 if (ishead && (mailp->m_flag & MSTATUS)) 231 printf(gettext("failed to fix up status field\n")); 232 return(lc); 233 } 234 235 /* 236 * Test if the passed line is a header line, RFC 822 style. 237 */ 238 int 239 headerp(register char *line) 240 { 241 register char *cp = line; 242 243 while (*cp && *cp != ' ' && *cp != '\t' && *cp != ':') 244 cp++; 245 return(*cp == ':'); 246 } 247 248 /* 249 * Output a reasonable looking status field. 250 * But if "status" is ignored and doign, forget it. 251 */ 252 static void 253 statusput( 254 register struct message *mp, 255 register FILE *obuf, 256 int doign, 257 int (*fp)(const char *, FILE *)) 258 { 259 char statout[12]; 260 261 if (doign && isign("status", 0)) 262 return; 263 if ((mp->m_flag & (MNEW|MREAD)) == MNEW) 264 return; 265 strcpy(statout, "Status: "); 266 if (mp->m_flag & MREAD) 267 strcat(statout, "R"); 268 if ((mp->m_flag & MNEW) == 0) 269 strcat(statout, "O"); 270 strcat(statout, "\n"); 271 (*fp)(statout, obuf); 272 } 273 274 /* 275 * Interface between the argument list and the mail1 routine 276 * which does all the dirty work. 277 */ 278 279 int 280 mail(char **people) 281 { 282 register char *cp2, *cp3; 283 register int s; 284 char *buf, **ap; 285 struct header head; 286 287 for (s = 0, ap = people; *ap; ap++) 288 s += strlen(*ap) + 2; 289 buf = (char *)salloc((unsigned)(s+1)); 290 cp2 = buf; 291 for (ap = people; *ap; ap++) { 292 for (cp3 = *ap; *cp3; ) { 293 if (*cp3 == ' ' || *cp3 == '\t') { 294 *cp3++ = ','; 295 while (*cp3 == ' ' || *cp3 == '\t') 296 cp3++; 297 } else 298 cp3++; 299 } 300 cp2 = copy(*ap, cp2); 301 *cp2++ = ','; 302 *cp2++ = ' '; 303 } 304 *cp2 = '\0'; 305 head.h_to = buf; 306 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; 307 head.h_others = NOSTRPTR; 308 head.h_seq = 0; 309 mail1(&head, Fflag, NOSTR); 310 return(0); 311 } 312 313 int 314 sendm(char *str) 315 { 316 if (value("flipm") != NOSTR) 317 return(Sendmail(str)); 318 else return(sendmail(str)); 319 } 320 321 int 322 Sendm(char *str) 323 { 324 if (value("flipm") != NOSTR) 325 return(sendmail(str)); 326 else return(Sendmail(str)); 327 } 328 329 /* 330 * Interface to the mail1 routine for the -t flag 331 * (read headers from text). 332 */ 333 int 334 tmail(void) 335 { 336 struct header head; 337 338 head.h_to = NOSTR; 339 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; 340 head.h_others = NOSTRPTR; 341 head.h_seq = 0; 342 mail1(&head, Fflag, NOSTR); 343 return(0); 344 } 345 346 /* 347 * Send mail to a bunch of user names. The interface is through 348 * the mail routine below. 349 */ 350 static int 351 sendmail(char *str) 352 { 353 struct header head; 354 355 if (blankline(str)) 356 head.h_to = NOSTR; 357 else 358 head.h_to = addto(NOSTR, str); 359 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; 360 head.h_others = NOSTRPTR; 361 head.h_seq = 0; 362 mail1(&head, 0, NOSTR); 363 return(0); 364 } 365 366 /* 367 * Send mail to a bunch of user names. The interface is through 368 * the mail routine below. 369 * save a copy of the letter 370 */ 371 static int 372 Sendmail(char *str) 373 { 374 struct header head; 375 376 if (blankline(str)) 377 head.h_to = NOSTR; 378 else 379 head.h_to = addto(NOSTR, str); 380 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR; 381 head.h_others = NOSTRPTR; 382 head.h_seq = 0; 383 mail1(&head, 1, NOSTR); 384 return(0); 385 } 386 387 /* 388 * Walk the list of fds, closing all but one. 389 */ 390 static int 391 closefd_walk(void *special_fd, int fd) 392 { 393 if (fd > STDERR_FILENO && fd != *(int *)special_fd) 394 (void) close(fd); 395 return (0); 396 } 397 398 /* 399 * Mail a message on standard input to the people indicated 400 * in the passed header. (Internal interface). 401 */ 402 void 403 mail1(struct header *hp, int use_to, char *orig_to) 404 { 405 pid_t p, pid; 406 int i, s, gotcha; 407 char **namelist, *deliver; 408 struct name *to, *np; 409 FILE *mtf, *fp; 410 int remote = rflag != NOSTR || rmail; 411 char **t; 412 char *deadletter; 413 char recfile[PATHSIZE]; 414 415 /* 416 * Collect user's mail from standard input. 417 * Get the result as mtf. 418 */ 419 420 pid = (pid_t)-1; 421 if ((mtf = collect(hp)) == NULL) 422 return; 423 hp->h_seq = 1; 424 if (hp->h_subject == NOSTR) 425 hp->h_subject = sflag; 426 if (fsize(mtf) == 0 && hp->h_subject == NOSTR) { 427 printf(gettext("No message !?!\n")); 428 goto out; 429 } 430 if (intty) { 431 printf(gettext("EOT\n")); 432 flush(); 433 } 434 435 /* 436 * If we need to use the To: line to determine the record 437 * file, save a copy of it before it's sorted below. 438 */ 439 440 if (use_to && orig_to == NOSTR && hp->h_to != NOSTR) 441 orig_to = strcpy((char *)salloc(strlen(hp->h_to)+1), hp->h_to); 442 else if (orig_to == NOSTR) 443 orig_to = ""; 444 445 /* 446 * Now, take the user names from the combined 447 * to and cc lists and do all the alias 448 * processing. 449 */ 450 451 senderr = 0; 452 to = cat(extract(hp->h_bcc, GBCC), 453 cat(extract(hp->h_to, GTO), 454 extract(hp->h_cc, GCC))); 455 to = translate(outpre(elide(usermap(to)))); 456 if (!senderr) 457 mapf(to, myname); 458 mechk(to); 459 for (gotcha = 0, np = to; np != NIL; np = np->n_flink) 460 if ((np->n_type & GDEL) == 0) 461 gotcha++; 462 hp->h_to = detract(to, GTO); 463 hp->h_cc = detract(to, GCC); 464 hp->h_bcc = detract(to, GBCC); 465 if ((mtf = infix(hp, mtf)) == NULL) { 466 fprintf(stderr, gettext(". . . message lost, sorry.\n")); 467 return; 468 } 469 rewind(mtf); 470 if (askme && isatty(0)) { 471 char ans[64]; 472 puthead(hp, stdout, GTO|GCC|GBCC, 0); 473 printf(gettext("Send? ")); 474 printf("[yes] "); 475 if (fgets(ans, sizeof(ans), stdin) && ans[0] && 476 (tolower(ans[0]) != 'y' && ans[0] != '\n')) 477 goto dead; 478 } 479 if (senderr) 480 goto dead; 481 /* 482 * Look through the recipient list for names with /'s 483 * in them which we write to as files directly. 484 */ 485 i = outof(to, mtf); 486 rewind(mtf); 487 if (!gotcha && !i) { 488 printf(gettext("No recipients specified\n")); 489 goto dead; 490 } 491 if (senderr) 492 goto dead; 493 494 getrecf(orig_to, recfile, use_to, sizeof (recfile)); 495 if (recfile != NOSTR && *recfile) 496 savemail(safeexpand(recfile), hp, mtf); 497 if (!gotcha) 498 goto out; 499 namelist = unpack(to); 500 if (debug) { 501 fprintf(stderr, "Recipients of message:\n"); 502 for (t = namelist; *t != NOSTR; t++) 503 fprintf(stderr, " \"%s\"", *t); 504 fprintf(stderr, "\n"); 505 return; 506 } 507 508 /* 509 * Wait, to absorb a potential zombie, then 510 * fork, set up the temporary mail file as standard 511 * input for "mail" and exec with the user list we generated 512 * far above. Return the process id to caller in case it 513 * wants to await the completion of mail. 514 */ 515 516 #ifdef VMUNIX 517 while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0) 518 ; 519 #else 520 #ifdef preSVr4 521 wait((int *)0); 522 #else 523 while (waitpid((pid_t)-1, (int *)0, WNOHANG) > 0) 524 ; 525 #endif 526 #endif 527 rewind(mtf); 528 pid = fork(); 529 if (pid == (pid_t)-1) { 530 perror("fork"); 531 dead: 532 deadletter = Getf("DEAD"); 533 if (fp = fopen(deadletter, 534 value("appenddeadletter") == NOSTR ? "w" : "a")) { 535 chmod(deadletter, DEADPERM); 536 puthead(hp, fp, GMASK|GCLEN, fsize(mtf) - textpos); 537 fseek(mtf, textpos, 0); 538 lcwrite(deadletter, mtf, fp, 539 value("appenddeadletter") != NOSTR); 540 fclose(fp); 541 } else 542 perror(deadletter); 543 goto out; 544 } 545 if (pid == 0) { 546 sigchild(); 547 #ifdef SIGTSTP 548 if (remote == 0) { 549 sigset(SIGTSTP, SIG_IGN); 550 sigset(SIGTTIN, SIG_IGN); 551 sigset(SIGTTOU, SIG_IGN); 552 } 553 #endif 554 sigset(SIGHUP, SIG_IGN); 555 sigset(SIGINT, SIG_IGN); 556 sigset(SIGQUIT, SIG_IGN); 557 s = fileno(mtf); 558 (void) fdwalk(closefd_walk, &s); 559 close(0); 560 dup(s); 561 close(s); 562 #ifdef CC 563 submit(getpid()); 564 #endif /* CC */ 565 if ((deliver = value("sendmail")) == NOSTR) 566 #ifdef SENDMAIL 567 deliver = SENDMAIL; 568 #else 569 deliver = MAIL; 570 #endif 571 execvp(safeexpand(deliver), namelist); 572 perror(deliver); 573 exit(1); 574 } 575 576 if (value("sendwait")!=NOSTR) 577 remote++; 578 out: 579 if (remote) { 580 while ((p = wait(&s)) != pid && p != (pid_t)-1) 581 ; 582 if (s != 0) 583 senderr++; 584 pid = 0; 585 } 586 fclose(mtf); 587 return; 588 } 589 590 /* 591 * Prepend a header in front of the collected stuff 592 * and return the new file. 593 */ 594 595 static FILE * 596 infix(struct header *hp, FILE *fi) 597 { 598 register FILE *nfo, *nfi; 599 register int c; 600 char *postmark, *returnaddr; 601 int fd = -1; 602 603 rewind(fi); 604 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 || 605 (nfo = fdopen(fd, "w")) == NULL) { 606 perror(tempMail); 607 return(fi); 608 } 609 if ((nfi = fopen(tempMail, "r")) == NULL) { 610 perror(tempMail); 611 fclose(nfo); 612 return(fi); 613 } 614 removefile(tempMail); 615 postmark = value("postmark"); 616 returnaddr = value("returnaddr"); 617 if ((postmark != NOSTR) || (returnaddr != NOSTR)) { 618 if (returnaddr && *returnaddr) 619 fprintf(nfo, "From: %s", returnaddr); 620 else 621 fprintf(nfo, "From: %s@%s", myname, host); 622 if (postmark && *postmark) 623 fprintf(nfo, " (%s)", postmark); 624 putc('\n', nfo); 625 } 626 puthead(hp, nfo, (GMASK & ~GBCC) | GCLEN, fsize(fi)); 627 textpos = ftell(nfo); 628 while ((c = getc(fi)) != EOF) 629 putc(c, nfo); 630 if (ferror(fi)) { 631 perror("read"); 632 return(fi); 633 } 634 fflush(nfo); 635 if (fferror(nfo)) { 636 perror(tempMail); 637 fclose(nfo); 638 fclose(nfi); 639 return(fi); 640 } 641 fclose(nfo); 642 fclose(fi); 643 rewind(nfi); 644 return(nfi); 645 } 646 647 /* 648 * Dump the message header on the 649 * passed file buffer. 650 */ 651 652 int 653 puthead(struct header *hp, FILE *fo, int w, long clen) 654 { 655 register int gotcha; 656 657 gotcha = 0; 658 if (hp->h_to != NOSTR && (w & GTO)) 659 fprintf(fo, "To: "), fmt(hp->h_to, fo), gotcha++; 660 if ((w & GSUBJECT) && (int)value("bsdcompat")) 661 if (hp->h_subject != NOSTR && *hp->h_subject) 662 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 663 else 664 if (sflag && *sflag) 665 fprintf(fo, "Subject: %s\n", sflag), gotcha++; 666 if (hp->h_cc != NOSTR && (w & GCC)) 667 fprintf(fo, "Cc: "), fmt(hp->h_cc, fo), gotcha++; 668 if (hp->h_bcc != NOSTR && (w & GBCC)) 669 fprintf(fo, "Bcc: "), fmt(hp->h_bcc, fo), gotcha++; 670 if (hp->h_defopt != NOSTR && (w & GDEFOPT)) 671 if (receipt_flg) 672 fprintf(fo, "Return-Receipt-To: %s\n", 673 hp->h_defopt), gotcha++; 674 else 675 fprintf(fo, "Default-Options: %s\n", hp->h_defopt), gotcha++; 676 if ((w & GSUBJECT) && !(int)value("bsdcompat")) 677 if (hp->h_subject != NOSTR && *hp->h_subject) 678 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 679 else 680 if (sflag && *sflag) 681 fprintf(fo, "Subject: %s\n", sflag), gotcha++; 682 if (hp->h_others != NOSTRPTR && (w & GOTHER)) { 683 char **p; 684 for (p = hp->h_others; *p; p++) 685 fprintf(fo, "%s\n", *p); 686 gotcha++; 687 } 688 #ifndef preSVr4 689 if (w & GCLEN) 690 fprintf(fo, "Content-Length: %ld\n", clen), gotcha++; 691 #endif 692 if (gotcha && (w & GNL)) 693 putc('\n', fo); 694 return(0); 695 } 696 697 /* 698 * Format the given text to not exceed 78 characters. 699 */ 700 static void 701 fmt(register char *str, register FILE *fo) 702 { 703 register int col = 4; 704 char name[256]; 705 int len; 706 707 str = strcpy((char *)salloc(strlen(str)+1), str); 708 while (str = yankword(str, name, sizeof (name), 1)) { 709 len = strlen(name); 710 if (col > 4) { 711 if (col + len > 76) { 712 fputs(",\n ", fo); 713 col = 4; 714 } else { 715 fputs(", ", fo); 716 col += 2; 717 } 718 } 719 fputs(name, fo); 720 col += len; 721 } 722 putc('\n', fo); 723 } 724 725 /* 726 * Save the outgoing mail on the passed file. 727 */ 728 static int 729 savemail(char name[], struct header *hp, FILE *fi) 730 { 731 register FILE *fo; 732 time_t now; 733 char *n; 734 #ifdef preSVr4 735 char line[BUFSIZ]; 736 #else 737 int c; 738 #endif 739 740 if (debug) 741 fprintf(stderr, gettext("save in '%s'\n"), name); 742 if ((fo = fopen(name, "a")) == NULL) { 743 perror(name); 744 return(-1); 745 } 746 time(&now); 747 n = rflag; 748 if (n == NOSTR) 749 n = myname; 750 fprintf(fo, "From %s %s", n, ctime(&now)); 751 puthead(hp, fo, GMASK|GCLEN, fsize(fi) - textpos); 752 fseek(fi, textpos, 0); 753 #ifdef preSVr4 754 while (fgets(line, sizeof line, fi)) { 755 if (!strncmp(line, "From ", 5)) 756 putc('>', fo); 757 fputs(line, fo); 758 } 759 #else 760 while ((c = getc(fi)) != EOF) 761 putc(c, fo); 762 #endif 763 putc('\n', fo); 764 fflush(fo); 765 if (fferror(fo)) 766 perror(name); 767 fclose(fo); 768 return(0); 769 } 770