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