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