1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 #if 0 36 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93"; 37 #endif 38 #endif /* not lint */ 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 42 #include "rcv.h" 43 #include "extern.h" 44 45 /* 46 * Mail -- a mail program 47 * 48 * Mail to others. 49 */ 50 51 /* 52 * Send message described by the passed pointer to the 53 * passed output buffer. Return -1 on error. 54 * Adjust the status: field if need be. 55 * If doign is given, suppress ignored header fields. 56 * prefix is a string to prepend to each output line. 57 */ 58 int 59 sendmessage(mp, obuf, doign, prefix) 60 struct message *mp; 61 FILE *obuf; 62 struct ignoretab *doign; 63 char *prefix; 64 { 65 long count; 66 FILE *ibuf; 67 char *cp, *cp2, line[LINESIZE]; 68 int ishead, infld, ignoring, dostat, firstline; 69 int c, length, prefixlen; 70 71 /* 72 * Compute the prefix string, without trailing whitespace 73 */ 74 if (prefix != NULL) { 75 cp2 = 0; 76 for (cp = prefix; *cp != '\0'; cp++) 77 if (*cp != ' ' && *cp != '\t') 78 cp2 = cp; 79 prefixlen = cp2 == NULL ? 0 : cp2 - prefix + 1; 80 } 81 ibuf = setinput(mp); 82 count = mp->m_size; 83 ishead = 1; 84 dostat = doign == 0 || !isign("status", doign); 85 infld = 0; 86 firstline = 1; 87 /* 88 * Process headers first 89 */ 90 while (count > 0 && ishead) { 91 if (fgets(line, sizeof(line), ibuf) == NULL) 92 break; 93 count -= length = strlen(line); 94 if (firstline) { 95 /* 96 * First line is the From line, so no headers 97 * there to worry about 98 */ 99 firstline = 0; 100 ignoring = doign == ignoreall; 101 } else if (line[0] == '\n') { 102 /* 103 * If line is blank, we've reached end of 104 * headers, so force out status: field 105 * and note that we are no longer in header 106 * fields 107 */ 108 if (dostat) { 109 statusput(mp, obuf, prefix); 110 dostat = 0; 111 } 112 ishead = 0; 113 ignoring = doign == ignoreall; 114 } else if (infld && (line[0] == ' ' || line[0] == '\t')) { 115 /* 116 * If this line is a continuation (via space or tab) 117 * of a previous header field, just echo it 118 * (unless the field should be ignored). 119 * In other words, nothing to do. 120 */ 121 } else { 122 /* 123 * Pick up the header field if we have one. 124 */ 125 for (cp = line; (c = *cp++) != '\0' && c != ':' && 126 !isspace((unsigned char)c);) 127 ; 128 cp2 = --cp; 129 while (isspace((unsigned char)*cp++)) 130 ; 131 if (cp[-1] != ':') { 132 /* 133 * Not a header line, force out status: 134 * This happens in uucp style mail where 135 * there are no headers at all. 136 */ 137 if (dostat) { 138 statusput(mp, obuf, prefix); 139 dostat = 0; 140 } 141 if (doign != ignoreall) 142 /* add blank line */ 143 (void)putc('\n', obuf); 144 ishead = 0; 145 ignoring = 0; 146 } else { 147 /* 148 * If it is an ignored field and 149 * we care about such things, skip it. 150 */ 151 *cp2 = '\0'; /* temporarily null terminate */ 152 if (doign && isign(line, doign)) 153 ignoring = 1; 154 else if ((line[0] == 's' || line[0] == 'S') && 155 strcasecmp(line, "status") == 0) { 156 /* 157 * If the field is "status," go compute 158 * and print the real Status: field 159 */ 160 if (dostat) { 161 statusput(mp, obuf, prefix); 162 dostat = 0; 163 } 164 ignoring = 1; 165 } else { 166 ignoring = 0; 167 *cp2 = c; /* restore */ 168 } 169 infld = 1; 170 } 171 } 172 if (!ignoring) { 173 /* 174 * Strip trailing whitespace from prefix 175 * if line is blank. 176 */ 177 if (prefix != NULL) { 178 if (length > 1) 179 fputs(prefix, obuf); 180 else 181 (void)fwrite(prefix, sizeof(*prefix), 182 prefixlen, obuf); 183 } 184 (void)fwrite(line, sizeof(*line), length, obuf); 185 if (ferror(obuf)) 186 return (-1); 187 } 188 } 189 /* 190 * Copy out message body 191 */ 192 if (doign == ignoreall) 193 count--; /* skip final blank line */ 194 if (prefix != NULL) 195 while (count > 0) { 196 if (fgets(line, sizeof(line), ibuf) == NULL) { 197 c = 0; 198 break; 199 } 200 count -= c = strlen(line); 201 /* 202 * Strip trailing whitespace from prefix 203 * if line is blank. 204 */ 205 if (c > 1) 206 fputs(prefix, obuf); 207 else 208 (void)fwrite(prefix, sizeof(*prefix), 209 prefixlen, obuf); 210 (void)fwrite(line, sizeof(*line), c, obuf); 211 if (ferror(obuf)) 212 return (-1); 213 } 214 else 215 while (count > 0) { 216 c = count < LINESIZE ? count : LINESIZE; 217 if ((c = fread(line, sizeof(*line), c, ibuf)) <= 0) 218 break; 219 count -= c; 220 if (fwrite(line, sizeof(*line), c, obuf) != c) 221 return (-1); 222 } 223 if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 224 /* no final blank line */ 225 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 226 return (-1); 227 return (0); 228 } 229 230 /* 231 * Output a reasonable looking status field. 232 */ 233 void 234 statusput(mp, obuf, prefix) 235 struct message *mp; 236 FILE *obuf; 237 char *prefix; 238 { 239 char statout[3]; 240 char *cp = statout; 241 242 if (mp->m_flag & MREAD) 243 *cp++ = 'R'; 244 if ((mp->m_flag & MNEW) == 0) 245 *cp++ = 'O'; 246 *cp = '\0'; 247 if (statout[0] != '\0') 248 fprintf(obuf, "%sStatus: %s\n", 249 prefix == NULL ? "" : prefix, statout); 250 } 251 252 /* 253 * Interface between the argument list and the mail1 routine 254 * which does all the dirty work. 255 */ 256 int 257 mail(to, cc, bcc, smopts, subject, replyto) 258 struct name *to, *cc, *bcc, *smopts; 259 char *subject, *replyto; 260 { 261 struct header head; 262 263 head.h_to = to; 264 head.h_subject = subject; 265 head.h_cc = cc; 266 head.h_bcc = bcc; 267 head.h_smopts = smopts; 268 head.h_replyto = replyto; 269 head.h_inreplyto = NULL; 270 mail1(&head, 0); 271 return (0); 272 } 273 274 275 /* 276 * Send mail to a bunch of user names. The interface is through 277 * the mail routine below. 278 */ 279 int 280 sendmail(str) 281 char *str; 282 { 283 struct header head; 284 285 head.h_to = extract(str, GTO); 286 head.h_subject = NULL; 287 head.h_cc = NULL; 288 head.h_bcc = NULL; 289 head.h_smopts = NULL; 290 head.h_replyto = value("REPLYTO"); 291 head.h_inreplyto = NULL; 292 mail1(&head, 0); 293 return (0); 294 } 295 296 /* 297 * Mail a message on standard input to the people indicated 298 * in the passed header. (Internal interface). 299 */ 300 void 301 mail1(hp, printheaders) 302 struct header *hp; 303 int printheaders; 304 { 305 char *cp; 306 char *nbuf; 307 int pid; 308 char **namelist; 309 struct name *to, *nsto; 310 FILE *mtf; 311 312 /* 313 * Collect user's mail from standard input. 314 * Get the result as mtf. 315 */ 316 if ((mtf = collect(hp, printheaders)) == NULL) 317 return; 318 if (value("interactive") != NULL) { 319 if (value("askcc") != NULL || value("askbcc") != NULL) { 320 if (value("askcc") != NULL) 321 grabh(hp, GCC); 322 if (value("askbcc") != NULL) 323 grabh(hp, GBCC); 324 } else { 325 printf("EOT\n"); 326 (void)fflush(stdout); 327 } 328 } 329 if (fsize(mtf) == 0) { 330 if (value("dontsendempty") != NULL) 331 goto out; 332 if (hp->h_subject == NULL) 333 printf("No message, no subject; hope that's ok\n"); 334 else 335 printf("Null message body; hope that's ok\n"); 336 } 337 /* 338 * Now, take the user names from the combined 339 * to and cc lists and do all the alias 340 * processing. 341 */ 342 senderr = 0; 343 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 344 if (to == NULL) { 345 printf("No recipients specified\n"); 346 senderr++; 347 } 348 /* 349 * Look through the recipient list for names with /'s 350 * in them which we write to as files directly. 351 */ 352 to = outof(to, mtf, hp); 353 if (senderr) 354 savedeadletter(mtf); 355 to = elide(to); 356 if (count(to) == 0) 357 goto out; 358 if (value("recordrecip") != NULL) { 359 /* 360 * Before fixing the header, save old To:. 361 * We do this because elide above has sorted To: list, and 362 * we would like to save message in a file named by the first 363 * recipient the user has entered, not the one being the first 364 * after sorting happened. 365 */ 366 if ((nsto = malloc(sizeof(struct name))) == NULL) 367 err(1, "Out of memory"); 368 bcopy(hp->h_to, nsto, sizeof(struct name)); 369 } 370 fixhead(hp, to); 371 if ((mtf = infix(hp, mtf)) == NULL) { 372 fprintf(stderr, ". . . message lost, sorry.\n"); 373 return; 374 } 375 namelist = unpack(cat(hp->h_smopts, to)); 376 if (debug) { 377 char **t; 378 379 printf("Sendmail arguments:"); 380 for (t = namelist; *t != NULL; t++) 381 printf(" \"%s\"", *t); 382 printf("\n"); 383 goto out; 384 } 385 if (value("recordrecip") != NULL) { 386 /* 387 * Extract first recipient username from saved To: and use it 388 * as a filename. 389 */ 390 if ((nbuf = malloc(strlen(detract(nsto, 0)) + 1)) == NULL) 391 err(1, "Out of memory"); 392 if ((cp = yanklogin(detract(nsto, 0), nbuf)) != NULL) 393 (void)savemail(expand(nbuf), mtf); 394 free(nbuf); 395 free(nsto); 396 } else if ((cp = value("record")) != NULL) 397 (void)savemail(expand(cp), mtf); 398 /* 399 * Fork, set up the temporary mail file as standard 400 * input for "mail", and exec with the user list we generated 401 * far above. 402 */ 403 pid = fork(); 404 if (pid == -1) { 405 warn("fork"); 406 savedeadletter(mtf); 407 goto out; 408 } 409 if (pid == 0) { 410 sigset_t nset; 411 (void)sigemptyset(&nset); 412 (void)sigaddset(&nset, SIGHUP); 413 (void)sigaddset(&nset, SIGINT); 414 (void)sigaddset(&nset, SIGQUIT); 415 (void)sigaddset(&nset, SIGTSTP); 416 (void)sigaddset(&nset, SIGTTIN); 417 (void)sigaddset(&nset, SIGTTOU); 418 prepare_child(&nset, fileno(mtf), -1); 419 if ((cp = value("sendmail")) != NULL) 420 cp = expand(cp); 421 else 422 cp = _PATH_SENDMAIL; 423 execv(cp, namelist); 424 warn("%s", cp); 425 _exit(1); 426 } 427 if (value("verbose") != NULL) 428 (void)wait_child(pid); 429 else 430 free_child(pid); 431 out: 432 (void)Fclose(mtf); 433 } 434 435 /* 436 * Fix the header by glopping all of the expanded names from 437 * the distribution list into the appropriate fields. 438 */ 439 void 440 fixhead(hp, tolist) 441 struct header *hp; 442 struct name *tolist; 443 { 444 struct name *np; 445 446 hp->h_to = NULL; 447 hp->h_cc = NULL; 448 hp->h_bcc = NULL; 449 for (np = tolist; np != NULL; np = np->n_flink) { 450 /* Don't copy deleted addresses to the header */ 451 if (np->n_type & GDEL) 452 continue; 453 if ((np->n_type & GMASK) == GTO) 454 hp->h_to = 455 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 456 else if ((np->n_type & GMASK) == GCC) 457 hp->h_cc = 458 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 459 else if ((np->n_type & GMASK) == GBCC) 460 hp->h_bcc = 461 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 462 } 463 } 464 465 /* 466 * Prepend a header in front of the collected stuff 467 * and return the new file. 468 */ 469 FILE * 470 infix(hp, fi) 471 struct header *hp; 472 FILE *fi; 473 { 474 FILE *nfo, *nfi; 475 int c, fd; 476 char tempname[PATHSIZE]; 477 478 (void)snprintf(tempname, sizeof(tempname), 479 "%s/mail.RsXXXXXXXXXX", tmpdir); 480 if ((fd = mkstemp(tempname)) == -1 || 481 (nfo = Fdopen(fd, "w")) == NULL) { 482 warn("%s", tempname); 483 return (fi); 484 } 485 if ((nfi = Fopen(tempname, "r")) == NULL) { 486 warn("%s", tempname); 487 (void)Fclose(nfo); 488 (void)rm(tempname); 489 return (fi); 490 } 491 (void)rm(tempname); 492 (void)puthead(hp, nfo, 493 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA); 494 c = getc(fi); 495 while (c != EOF) { 496 (void)putc(c, nfo); 497 c = getc(fi); 498 } 499 if (ferror(fi)) { 500 warnx("read"); 501 rewind(fi); 502 return (fi); 503 } 504 (void)fflush(nfo); 505 if (ferror(nfo)) { 506 warn("%s", tempname); 507 (void)Fclose(nfo); 508 (void)Fclose(nfi); 509 rewind(fi); 510 return (fi); 511 } 512 (void)Fclose(nfo); 513 (void)Fclose(fi); 514 rewind(nfi); 515 return (nfi); 516 } 517 518 /* 519 * Dump the to, subject, cc header on the 520 * passed file buffer. 521 */ 522 int 523 puthead(hp, fo, w) 524 struct header *hp; 525 FILE *fo; 526 int w; 527 { 528 int gotcha; 529 530 gotcha = 0; 531 if (hp->h_to != NULL && w & GTO) 532 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 533 if (hp->h_subject != NULL && w & GSUBJECT) 534 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 535 if (hp->h_cc != NULL && w & GCC) 536 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 537 if (hp->h_bcc != NULL && w & GBCC) 538 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 539 if (hp->h_replyto != NULL && w & GREPLYTO) 540 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; 541 if (hp->h_inreplyto != NULL && w & GINREPLYTO) 542 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; 543 if (gotcha && w & GNL) 544 (void)putc('\n', fo); 545 return (0); 546 } 547 548 /* 549 * Format the given header line to not exceed 72 characters. 550 */ 551 void 552 fmt(str, np, fo, comma) 553 const char *str; 554 struct name *np; 555 FILE *fo; 556 int comma; 557 { 558 int col, len; 559 560 comma = comma ? 1 : 0; 561 col = strlen(str); 562 if (col) 563 fputs(str, fo); 564 for (; np != NULL; np = np->n_flink) { 565 if (np->n_flink == NULL) 566 comma = 0; 567 len = strlen(np->n_name); 568 col++; /* for the space */ 569 if (col + len + comma > 72 && col > 4) { 570 fprintf(fo, "\n "); 571 col = 4; 572 } else 573 fprintf(fo, " "); 574 fputs(np->n_name, fo); 575 if (comma) 576 fprintf(fo, ","); 577 col += len + comma; 578 } 579 fprintf(fo, "\n"); 580 } 581 582 /* 583 * Save the outgoing mail on the passed file. 584 */ 585 586 /*ARGSUSED*/ 587 int 588 savemail(name, fi) 589 char name[]; 590 FILE *fi; 591 { 592 FILE *fo; 593 char buf[BUFSIZ]; 594 int i; 595 time_t now; 596 597 if ((fo = Fopen(name, "a")) == NULL) { 598 warn("%s", name); 599 return (-1); 600 } 601 (void)time(&now); 602 fprintf(fo, "From %s %s", myname, ctime(&now)); 603 while ((i = fread(buf, 1, sizeof(buf), fi)) > 0) 604 (void)fwrite(buf, 1, i, fo); 605 fprintf(fo, "\n"); 606 (void)fflush(fo); 607 if (ferror(fo)) 608 warn("%s", name); 609 (void)Fclose(fo); 610 rewind(fi); 611 return (0); 612 } 613