1 /* $NetBSD: send.c,v 1.39 2017/11/09 20:27:50 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93"; 36 #else 37 __RCSID("$NetBSD: send.c,v 1.39 2017/11/09 20:27:50 christos Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <assert.h> 42 43 #include "rcv.h" 44 #include "extern.h" 45 #ifdef MIME_SUPPORT 46 #include "mime.h" 47 #endif 48 #include "sig.h" 49 50 /* 51 * Mail -- a mail program 52 * 53 * Mail to others. 54 */ 55 56 /* 57 * compare twwo name structures. Support for get_smopts. 58 */ 59 static int 60 namecmp(struct name *np1, struct name *np2) 61 { 62 for (/*EMPTY*/; np1 && np2; np1 = np1->n_flink, np2 = np2->n_flink) { 63 if (strcmp(np1->n_name, np2->n_name) != 0) 64 return 1; 65 } 66 return np1 || np2; 67 } 68 69 /* 70 * Get the sendmail options from the smopts list. 71 */ 72 static struct name * 73 get_smopts(struct name *to) 74 { 75 struct smopts_s *sp; 76 struct name *smargs, *np; 77 smargs = NULL; 78 for (np = to; np; np = np->n_flink) { 79 if ((sp = findsmopts(np->n_name, 0)) == NULL) 80 continue; 81 if (smargs == NULL) 82 smargs = sp->s_smopts; 83 else if (namecmp(smargs, sp->s_smopts) != 0) 84 return NULL; 85 } 86 if (smargs && 87 smargs->n_flink == NULL && 88 (smargs->n_name == NULL || smargs->n_name[0] == '\0')) 89 return NULL; 90 return smargs; 91 } 92 93 /* 94 * Output a reasonable looking status field. 95 */ 96 static void 97 statusput(struct message *mp, FILE *obuf, const char *prefix) 98 { 99 char statout[3]; 100 char *cp = statout; 101 102 if (mp->m_flag & MREAD) 103 *cp++ = 'R'; 104 if ((mp->m_flag & MNEW) == 0) 105 *cp++ = 'O'; 106 *cp = 0; 107 if (statout[0]) 108 (void)fprintf(obuf, "%sStatus: %s\n", 109 prefix == NULL ? "" : prefix, statout); 110 } 111 112 /* 113 * Send message described by the passed pointer to the 114 * passed output buffer. Return -1 on error. 115 * Adjust the status: field if need be. 116 * If doign is given, suppress ignored header fields. 117 * prefix is a string to prepend to each output line. 118 */ 119 PUBLIC int 120 sendmessage(struct message *mp, FILE *obuf, struct ignoretab *doign, 121 const char *prefix, struct mime_info *mip) 122 { 123 off_t len; 124 FILE *ibuf; 125 char line[LINESIZE]; 126 int isheadflag, infld, ignoring, dostat, firstline; 127 char *cp; 128 size_t prefixlen; 129 size_t linelen; 130 int rval; 131 132 ignoring = 0; 133 prefixlen = 0; 134 rval = -1; 135 136 /* 137 * Compute the prefix string, without trailing whitespace 138 */ 139 if (prefix != NULL) { 140 const char *dp, *dp2 = NULL; 141 for (dp = prefix; *dp; dp++) 142 if (!is_WSP(*dp)) 143 dp2 = dp; 144 prefixlen = dp2 == 0 ? 0 : dp2 - prefix + 1; 145 } 146 ibuf = setinput(mp); 147 len = mp->m_size; 148 isheadflag = 1; 149 dostat = doign == 0 || !isign("status", doign); 150 infld = 0; 151 firstline = 1; 152 153 sig_check(); 154 /* 155 * Process headers first 156 */ 157 #ifdef MIME_SUPPORT 158 if (mip) 159 obuf = mime_decode_header(mip); 160 #endif 161 while (len > 0 && isheadflag) { 162 sig_check(); 163 if (fgets(line, (int)sizeof(line), ibuf) == NULL) 164 break; 165 len -= linelen = strlen(line); 166 if (firstline) { 167 /* 168 * First line is the From line, so no headers 169 * there to worry about 170 */ 171 firstline = 0; 172 ignoring = doign == ignoreall || doign == bouncetab; 173 #ifdef MIME_SUPPORT 174 /* XXX - ignore multipart boundary lines! */ 175 if (line[0] == '-' && line[1] == '-') 176 ignoring = 1; 177 #endif 178 } else if (line[0] == '\n') { 179 /* 180 * If line is blank, we've reached end of 181 * headers, so force out status: field 182 * and note that we are no longer in header 183 * fields 184 */ 185 if (dostat) { 186 statusput(mp, obuf, prefix); 187 dostat = 0; 188 } 189 isheadflag = 0; 190 ignoring = doign == ignoreall; 191 } else if (infld && is_WSP(line[0])) { 192 /* 193 * If this line is a continuation (via space or tab) 194 * of a previous header field, just echo it 195 * (unless the field should be ignored). 196 * In other words, nothing to do. 197 */ 198 } else { 199 /* 200 * Pick up the header field if we have one. 201 */ 202 char *save_cp; 203 for (cp = line; *cp && *cp != ':' && !is_WSP(*cp); cp++) 204 continue; 205 save_cp = cp; 206 cp = skip_WSP(cp); 207 if (*cp++ != ':') { 208 /* 209 * Not a header line, force out status: 210 * This happens in uucp style mail where 211 * there are no headers at all. 212 */ 213 if (dostat) { 214 statusput(mp, obuf, prefix); 215 dostat = 0; 216 } 217 if (doign != ignoreall) 218 /* add blank line */ 219 (void)putc('\n', obuf); 220 isheadflag = 0; 221 ignoring = 0; 222 } else { 223 /* 224 * If it is an ignored field and 225 * we care about such things, skip it. 226 */ 227 int save_c; 228 save_c = *save_cp; 229 *save_cp = 0; /* temporarily null terminate */ 230 if (doign && isign(line, doign)) 231 ignoring = 1; 232 else if ((line[0] == 's' || line[0] == 'S') && 233 strcasecmp(line, "status") == 0) { 234 /* 235 * If the field is "status," go compute 236 * and print the real Status: field 237 */ 238 if (dostat) { 239 statusput(mp, obuf, prefix); 240 dostat = 0; 241 } 242 ignoring = 1; 243 } else { 244 ignoring = 0; 245 } 246 infld = 1; 247 *save_cp = save_c; /* restore the line */ 248 } 249 } 250 if (!ignoring) { 251 /* 252 * Strip trailing whitespace from prefix 253 * if line is blank. 254 */ 255 if (prefix != NULL) { 256 if (linelen > 1) 257 (void)fputs(prefix, obuf); 258 else 259 (void)fwrite(prefix, sizeof(*prefix), 260 prefixlen, obuf); 261 } 262 (void)fwrite(line, sizeof(*line), linelen, obuf); 263 if (ferror(obuf)) 264 goto out; 265 } 266 } 267 sig_check(); 268 269 /* 270 * Copy out message body 271 */ 272 #ifdef MIME_SUPPORT 273 if (mip) { 274 obuf = mime_decode_body(mip); 275 sig_check(); 276 if (obuf == NULL) { /* XXX - early out */ 277 rval = 0; 278 goto out; 279 } 280 } 281 #endif 282 if (doign == ignoreall) 283 len--; /* skip final blank line */ 284 285 linelen = 0; /* needed for in case len == 0 */ 286 if (prefix != NULL) { 287 while (len > 0) { 288 sig_check(); 289 if (fgets(line, (int)sizeof(line), ibuf) == NULL) { 290 linelen = 0; 291 break; 292 } 293 len -= linelen = strlen(line); 294 /* 295 * Strip trailing whitespace from prefix 296 * if line is blank. 297 */ 298 if (linelen > 1) 299 (void)fputs(prefix, obuf); 300 else 301 (void)fwrite(prefix, sizeof(*prefix), 302 prefixlen, obuf); 303 (void)fwrite(line, sizeof(*line), linelen, obuf); 304 if (ferror(obuf)) 305 goto out; 306 } 307 } 308 else { 309 while (len > 0) { 310 sig_check(); 311 linelen = (size_t)len < sizeof(line) ? (size_t)len : sizeof(line); 312 if ((linelen = fread(line, sizeof(*line), linelen, ibuf)) == 0) { 313 break; 314 } 315 len -= linelen; 316 if (fwrite(line, sizeof(*line), linelen, obuf) != linelen) 317 goto out; 318 } 319 } 320 sig_check(); 321 if (doign == ignoreall && linelen > 0 && line[linelen - 1] != '\n') { 322 int c; 323 324 /* no final blank line */ 325 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 326 goto out; 327 } 328 rval = 0; 329 out: 330 sig_check(); 331 return rval; 332 } 333 334 /* 335 * Fix the header by glopping all of the expanded names from 336 * the distribution list into the appropriate fields. 337 */ 338 static void 339 fixhead(struct header *hp, struct name *tolist) 340 { 341 struct name *np; 342 343 hp->h_to = NULL; 344 hp->h_cc = NULL; 345 hp->h_bcc = NULL; 346 for (np = tolist; np != NULL; np = np->n_flink) { 347 if (np->n_type & GDEL) 348 continue; /* Don't copy deleted addresses to the header */ 349 if ((np->n_type & GMASK) == GTO) 350 hp->h_to = 351 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 352 else if ((np->n_type & GMASK) == GCC) 353 hp->h_cc = 354 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 355 else if ((np->n_type & GMASK) == GBCC) 356 hp->h_bcc = 357 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 358 } 359 } 360 361 /* 362 * Format the given header line to not exceed 72 characters. 363 */ 364 static void 365 fmt(const char *str, struct name *np, FILE *fo, size_t comma) 366 { 367 size_t col; 368 size_t len; 369 370 comma = comma ? 1 : 0; 371 col = strlen(str); 372 if (col) 373 (void)fputs(str, fo); 374 for (/*EMPTY*/; np != NULL; np = np->n_flink) { 375 if (np->n_flink == NULL) 376 comma = 0; 377 len = strlen(np->n_name); 378 col++; /* for the space */ 379 if (col + len + comma > 72 && col > 4) { 380 (void)fputs("\n ", fo); 381 col = 4; 382 } else 383 (void)putc(' ', fo); 384 (void)fputs(np->n_name, fo); 385 if (comma) 386 (void)putc(',', fo); 387 col += len + comma; 388 } 389 (void)putc('\n', fo); 390 } 391 392 /* 393 * Formatting for extra_header lines: try to wrap them before 72 394 * characters. 395 * 396 * XXX - should this allow for quoting? 397 */ 398 static void 399 fmt2(const char *str, FILE *fo) 400 { 401 const char *p; 402 size_t col; 403 404 col = 0; 405 p = strchr(str, ':'); 406 assert(p != NULL); /* this is a coding error */ 407 408 while (str <= p) { /* output the header name */ 409 (void)putc(*str, fo); 410 str++; 411 col++; 412 } 413 assert(is_WSP(*str)); /* this is a coding error */ 414 415 (void)putc(' ', fo); /* output a single space after the ':' */ 416 str = skip_WSP(str); 417 418 while (*str != '\0') { 419 if (p <= str) { 420 p = strpbrk(str, " \t"); 421 if (p == NULL) 422 p = str + strlen(str); 423 if (col + (p - str) > 72 && col > 4) { 424 (void)fputs("\n ", fo); 425 col = 4; 426 str = skip_WSP(str); 427 continue; 428 } 429 } 430 (void)putc(*str, fo); 431 str++; 432 col++; 433 } 434 (void)putc('\n', fo); 435 } 436 437 /* 438 * Dump the to, subject, cc header on the 439 * passed file buffer. 440 */ 441 PUBLIC int 442 puthead(struct header *hp, FILE *fo, int w) 443 { 444 struct name *np; 445 int gotcha; 446 447 gotcha = 0; 448 if (hp->h_to != NULL && w & GTO) 449 fmt("To:", hp->h_to, fo, w & GCOMMA), gotcha++; 450 if (hp->h_subject != NULL && w & GSUBJECT) 451 (void)fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 452 if (hp->h_cc != NULL && w & GCC) 453 fmt("Cc:", hp->h_cc, fo, w & GCOMMA), gotcha++; 454 if (hp->h_bcc != NULL && w & GBCC) 455 fmt("Bcc:", hp->h_bcc, fo, w & GCOMMA), gotcha++; 456 if (hp->h_in_reply_to != NULL && w & GMISC) 457 (void)fprintf(fo, "In-Reply-To: %s\n", hp->h_in_reply_to), gotcha++; 458 if (hp->h_references != NULL && w & GMISC) 459 fmt("References:", hp->h_references, fo, w & GCOMMA), gotcha++; 460 if (hp->h_extra != NULL && w & GMISC) { 461 for (np = hp->h_extra; np != NULL; np = np->n_flink) 462 fmt2(np->n_name, fo); 463 gotcha++; 464 } 465 if (hp->h_smopts != NULL && w & GSMOPTS) 466 (void)fprintf(fo, "(sendmail options: %s)\n", detract(hp->h_smopts, GSMOPTS)), gotcha++; 467 #ifdef MIME_SUPPORT 468 if (w & GMIME && (hp->h_attach || value(ENAME_MIME_ENCODE_MSG))) 469 mime_putheader(fo, hp), gotcha++; 470 #endif 471 if (gotcha && w & GNL) 472 (void)putc('\n', fo); 473 return 0; 474 } 475 476 /* 477 * Prepend a header in front of the collected stuff 478 * and return the new file. 479 */ 480 static FILE * 481 infix(struct header *hp, FILE *fi) 482 { 483 FILE *nfo, *nfi; 484 int c, fd; 485 char tempname[PATHSIZE]; 486 487 (void)snprintf(tempname, sizeof(tempname), 488 "%s/mail.RsXXXXXXXXXX", tmpdir); 489 if ((fd = mkstemp(tempname)) == -1 || 490 (nfo = Fdopen(fd, "wef")) == NULL) { 491 if (fd != -1) 492 (void)close(fd); 493 warn("%s", tempname); 494 return fi; 495 } 496 if ((nfi = Fopen(tempname, "ref")) == NULL) { 497 warn("%s", tempname); 498 (void)Fclose(nfo); 499 (void)rm(tempname); 500 return fi; 501 } 502 (void)rm(tempname); 503 (void)puthead(hp, nfo, 504 GTO | GSUBJECT | GCC | GBCC | GMISC | GNL | GCOMMA 505 #ifdef MIME_SUPPORT 506 | GMIME 507 #endif 508 ); 509 510 c = getc(fi); 511 while (c != EOF) { 512 (void)putc(c, nfo); 513 c = getc(fi); 514 } 515 if (ferror(fi)) { 516 warn("read"); 517 rewind(fi); 518 return fi; 519 } 520 (void)fflush(nfo); 521 if (ferror(nfo)) { 522 warn("%s", tempname); 523 (void)Fclose(nfo); 524 (void)Fclose(nfi); 525 rewind(fi); 526 return fi; 527 } 528 (void)Fclose(nfo); 529 (void)Fclose(fi); 530 rewind(nfi); 531 return nfi; 532 } 533 534 /* 535 * Save the outgoing mail on the passed file. 536 * 537 * Take care not to save a valid headline or the mbox file will be 538 * broken. Prefix valid headlines with '>'. 539 * 540 * Note: most servers prefix any line starting with "From " in the 541 * body of the message. We are more restrictive to avoid header/body 542 * issues. 543 */ 544 /*ARGSUSED*/ 545 static int 546 savemail(const char name[], FILE *fi) 547 { 548 FILE *fo; 549 time_t now; 550 mode_t m; 551 char *line; 552 size_t linelen; 553 int afterblank; 554 555 m = umask(077); 556 fo = Fopen(name, "aef"); 557 (void)umask(m); 558 if (fo == NULL) { 559 warn("%s", name); 560 return -1; 561 } 562 (void)time(&now); 563 (void)fprintf(fo, "From %s %s", myname, ctime(&now)); 564 afterblank = 0; 565 while ((line = fgetln(fi, &linelen)) != NULL) { 566 char c, *cp; 567 cp = line + linelen - 1; 568 if (afterblank && 569 linelen > sizeof("From . Aaa Aaa O0 00:00 0000") && 570 line[0] == 'F' && 571 line[1] == 'r' && 572 line[2] == 'o' && 573 line[3] == 'm' && 574 line[4] == ' ' && 575 *cp == '\n') { 576 c = *cp; 577 *cp = '\0'; 578 if (ishead(line)) 579 (void)fputc('>', fo); 580 *cp = c; 581 } 582 (void)fwrite(line, 1, linelen, fo); 583 afterblank = linelen == 1 && line[0] == '\n'; 584 } 585 (void)putc('\n', fo); 586 (void)fflush(fo); 587 if (ferror(fo)) 588 warn("%s", name); 589 (void)Fclose(fo); 590 rewind(fi); 591 return 0; 592 } 593 594 /* 595 * Mail a message that is already prepared in a file. 596 */ 597 PUBLIC void 598 mail2(FILE *mtf, const char **namelist) 599 { 600 int pid; 601 const char *cp; 602 603 if (debug) { 604 const char **t; 605 606 (void)printf("Sendmail arguments:"); 607 for (t = namelist; *t != NULL; t++) 608 (void)printf(" \"%s\"", *t); 609 (void)printf("\n"); 610 return; 611 } 612 if ((cp = value(ENAME_RECORD)) != NULL) 613 (void)savemail(expand(cp), mtf); 614 /* 615 * Fork, set up the temporary mail file as standard 616 * input for "mail", and exec with the user list we generated 617 * far above. 618 */ 619 pid = fork(); 620 switch (pid) { 621 case -1: 622 warn("fork"); 623 savedeadletter(mtf); 624 return; 625 case 0: 626 { 627 sigset_t nset; 628 (void)sigemptyset(&nset); 629 (void)sigaddset(&nset, SIGHUP); 630 (void)sigaddset(&nset, SIGINT); 631 (void)sigaddset(&nset, SIGQUIT); 632 (void)sigaddset(&nset, SIGTSTP); 633 (void)sigaddset(&nset, SIGTTIN); 634 (void)sigaddset(&nset, SIGTTOU); 635 prepare_child(&nset, fileno(mtf), -1); 636 if ((cp = value(ENAME_SENDMAIL)) != NULL) 637 cp = expand(cp); 638 else 639 cp = _PATH_SENDMAIL; 640 (void)execv(cp, (char *const *)__UNCONST(namelist)); 641 warn("%s", cp); 642 _exit(1); 643 break; /* Appease GCC */ 644 } 645 default: 646 if (value(ENAME_VERBOSE) != NULL) 647 (void)wait_child(pid); 648 else 649 free_child(pid); 650 break; 651 } 652 } 653 654 static struct name * 655 ncopy(struct name *np) 656 { 657 struct name *rv; 658 struct name *lp; 659 struct name *tp; 660 661 lp = NULL; /* XXX gcc -Wuninitialized sh3 */ 662 rv = NULL; 663 for (/*EMPTY*/; np; np = np->n_flink) { 664 tp = nalloc(np->n_name, np->n_type); 665 if (rv == NULL) 666 rv = tp; 667 else { 668 lp->n_flink = tp; 669 tp->n_blink = lp; 670 } 671 lp = tp; 672 } 673 return rv; 674 } 675 676 /* 677 * Mail a message on standard input to the people indicated 678 * in the passed header. (Internal interface). 679 */ 680 PUBLIC void 681 mail1(struct header *hp, int printheaders) 682 { 683 const char **namelist; 684 struct name *to; 685 FILE *mtf; 686 687 /* 688 * Collect user's mail from standard input. 689 * Get the result as mtf. 690 */ 691 if ((mtf = collect(hp, printheaders)) == NULL) 692 return; 693 694 /* 695 * Grab any extra header lines. Do this after collect() so 696 * that we can add header lines while collecting. 697 */ 698 hp->h_extra = ncopy(extra_headers); 699 700 if (value(ENAME_INTERACTIVE) != NULL) { 701 if (value(ENAME_ASKCC) != NULL || value(ENAME_ASKBCC) != NULL) { 702 if (value(ENAME_ASKCC) != NULL) 703 (void)grabh(hp, GCC); 704 if (value(ENAME_ASKBCC) != NULL) 705 (void)grabh(hp, GBCC); 706 } else { 707 (void)printf("EOT\n"); 708 (void)fflush(stdout); 709 } 710 } 711 if (fsize(mtf) == 0) { 712 if (value(ENAME_DONTSENDEMPTY) != NULL) 713 goto out; 714 if (hp->h_subject == NULL) 715 (void)printf("No message, no subject; hope that's ok\n"); 716 else 717 (void)printf("Null message body; hope that's ok\n"); 718 } 719 /* 720 * Now, take the user names from the combined 721 * to and cc lists and do all the alias 722 * processing. 723 */ 724 senderr = 0; 725 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 726 if (to == NULL) { 727 (void)printf("No recipients specified\n"); 728 senderr++; 729 } 730 #ifdef MIME_SUPPORT 731 /* 732 * If there are attachments, repackage the mail as a 733 * multi-part MIME message. 734 */ 735 if (hp->h_attach || value(ENAME_MIME_ENCODE_MSG)) 736 mtf = mime_encode(mtf, hp); 737 #endif 738 /* 739 * Look through the recipient list for names with /'s 740 * in them which we write to as files directly. 741 */ 742 to = outof(to, mtf, hp); 743 if (senderr) 744 savedeadletter(mtf); 745 to = elide(to); 746 if (count(to) == 0) 747 goto out; 748 fixhead(hp, to); 749 if ((mtf = infix(hp, mtf)) == NULL) { 750 (void)fprintf(stderr, ". . . message lost, sorry.\n"); 751 return; 752 } 753 if (hp->h_smopts == NULL) { 754 hp->h_smopts = get_smopts(to); 755 if (hp->h_smopts != NULL && 756 hp->h_smopts->n_name[0] != '\0' && 757 value(ENAME_SMOPTS_VERIFY) != NULL) 758 if (grabh(hp, GSMOPTS)) { 759 (void)printf("mail aborted!\n"); 760 savedeadletter(mtf); 761 goto out; 762 } 763 } 764 namelist = unpack(hp->h_smopts, to); 765 mail2(mtf, namelist); 766 out: 767 (void)Fclose(mtf); 768 } 769 770 /* 771 * Interface between the argument list and the mail1 routine 772 * which does all the dirty work. 773 */ 774 PUBLIC int 775 mail(struct name *to, struct name *cc, struct name *bcc, 776 struct name *smopts, char *subject, struct attachment *attach) 777 { 778 struct header head; 779 780 /* ensure that all header fields are initially NULL */ 781 (void)memset(&head, 0, sizeof(head)); 782 783 head.h_to = to; 784 head.h_subject = subject; 785 head.h_cc = cc; 786 head.h_bcc = bcc; 787 head.h_smopts = smopts; 788 #ifdef MIME_SUPPORT 789 head.h_attach = attach; 790 #endif 791 mail1(&head, 0); 792 return 0; 793 } 794 795 /* 796 * Send mail to a bunch of user names. The interface is through 797 * the mail1 routine above. 798 */ 799 PUBLIC int 800 sendmail(void *v) 801 { 802 char *str = v; 803 struct header head; 804 805 /* ensure that all header fields are initially NULL */ 806 (void)memset(&head, 0, sizeof(head)); 807 808 head.h_to = extract(str, GTO); 809 810 mail1(&head, 0); 811 return 0; 812 } 813