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 static const char rcsid[] = 39 "$FreeBSD$"; 40 #endif /* not lint */ 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 register struct message *mp; 61 FILE *obuf; 62 struct ignoretab *doign; 63 char *prefix; 64 { 65 long count; 66 register FILE *ibuf; 67 char line[LINESIZE]; 68 int ishead, infld, ignoring, dostat, firstline; 69 register char *cp, *cp2; 70 register int c; 71 int length; 72 int prefixlen; 73 74 /* 75 * Compute the prefix string, without trailing whitespace 76 */ 77 if (prefix != NOSTR) { 78 cp2 = 0; 79 for (cp = prefix; *cp; cp++) 80 if (*cp != ' ' && *cp != '\t') 81 cp2 = cp; 82 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; 83 } 84 ibuf = setinput(mp); 85 count = mp->m_size; 86 ishead = 1; 87 dostat = doign == 0 || !isign("status", doign); 88 infld = 0; 89 firstline = 1; 90 /* 91 * Process headers first 92 */ 93 while (count > 0 && ishead) { 94 if (fgets(line, sizeof(line), ibuf) == NULL) 95 break; 96 count -= length = strlen(line); 97 if (firstline) { 98 /* 99 * First line is the From line, so no headers 100 * there to worry about 101 */ 102 firstline = 0; 103 ignoring = doign == ignoreall; 104 } else if (line[0] == '\n') { 105 /* 106 * If line is blank, we've reached end of 107 * headers, so force out status: field 108 * and note that we are no longer in header 109 * fields 110 */ 111 if (dostat) { 112 statusput(mp, obuf, prefix); 113 dostat = 0; 114 } 115 ishead = 0; 116 ignoring = doign == ignoreall; 117 } else if (infld && (line[0] == ' ' || line[0] == '\t')) { 118 /* 119 * If this line is a continuation (via space or tab) 120 * of a previous header field, just echo it 121 * (unless the field should be ignored). 122 * In other words, nothing to do. 123 */ 124 } else { 125 /* 126 * Pick up the header field if we have one. 127 */ 128 for (cp = line; (c = *cp++) && c != ':' && !isspace(c);) 129 ; 130 cp2 = --cp; 131 while (isspace(*cp++)) 132 ; 133 if (cp[-1] != ':') { 134 /* 135 * Not a header line, force out status: 136 * This happens in uucp style mail where 137 * there are no headers at all. 138 */ 139 if (dostat) { 140 statusput(mp, obuf, prefix); 141 dostat = 0; 142 } 143 if (doign != ignoreall) 144 /* add blank line */ 145 (void) putc('\n', obuf); 146 ishead = 0; 147 ignoring = 0; 148 } else { 149 /* 150 * If it is an ignored field and 151 * we care about such things, skip it. 152 */ 153 *cp2 = 0; /* temporarily null terminate */ 154 if (doign && isign(line, doign)) 155 ignoring = 1; 156 else if ((line[0] == 's' || line[0] == 'S') && 157 strcasecmp(line, "status") == 0) { 158 /* 159 * If the field is "status," go compute 160 * and print the real Status: field 161 */ 162 if (dostat) { 163 statusput(mp, obuf, prefix); 164 dostat = 0; 165 } 166 ignoring = 1; 167 } else { 168 ignoring = 0; 169 *cp2 = c; /* restore */ 170 } 171 infld = 1; 172 } 173 } 174 if (!ignoring) { 175 /* 176 * Strip trailing whitespace from prefix 177 * if line is blank. 178 */ 179 if (prefix != NOSTR) { 180 if (length > 1) 181 fputs(prefix, obuf); 182 else 183 (void) fwrite(prefix, sizeof *prefix, 184 prefixlen, obuf); 185 } 186 (void) fwrite(line, sizeof *line, length, obuf); 187 if (ferror(obuf)) 188 return -1; 189 } 190 } 191 /* 192 * Copy out message body 193 */ 194 if (doign == ignoreall) 195 count--; /* skip final blank line */ 196 if (prefix != NOSTR) 197 while (count > 0) { 198 if (fgets(line, sizeof(line), ibuf) == NULL) { 199 c = 0; 200 break; 201 } 202 count -= c = strlen(line); 203 /* 204 * Strip trailing whitespace from prefix 205 * if line is blank. 206 */ 207 if (c > 1) 208 fputs(prefix, obuf); 209 else 210 (void) fwrite(prefix, sizeof *prefix, 211 prefixlen, obuf); 212 (void) fwrite(line, sizeof *line, c, obuf); 213 if (ferror(obuf)) 214 return -1; 215 } 216 else 217 while (count > 0) { 218 c = count < LINESIZE ? count : LINESIZE; 219 if ((c = fread(line, sizeof *line, c, ibuf)) <= 0) 220 break; 221 count -= c; 222 if (fwrite(line, sizeof *line, c, obuf) != c) 223 return -1; 224 } 225 if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 226 /* no final blank line */ 227 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 228 return -1; 229 return 0; 230 } 231 232 /* 233 * Output a reasonable looking status field. 234 */ 235 void 236 statusput(mp, obuf, prefix) 237 register struct message *mp; 238 FILE *obuf; 239 char *prefix; 240 { 241 char statout[3]; 242 register char *cp = statout; 243 244 if (mp->m_flag & MREAD) 245 *cp++ = 'R'; 246 if ((mp->m_flag & MNEW) == 0) 247 *cp++ = 'O'; 248 *cp = 0; 249 if (statout[0]) 250 fprintf(obuf, "%sStatus: %s\n", 251 prefix == NOSTR ? "" : prefix, statout); 252 } 253 254 /* 255 * Interface between the argument list and the mail1 routine 256 * which does all the dirty work. 257 */ 258 int 259 mail(to, cc, bcc, smopts, subject, replyto) 260 struct name *to, *cc, *bcc, *smopts; 261 char *subject, *replyto; 262 { 263 struct header head; 264 265 head.h_to = to; 266 head.h_subject = subject; 267 head.h_cc = cc; 268 head.h_bcc = bcc; 269 head.h_smopts = smopts; 270 head.h_replyto = replyto; 271 head.h_inreplyto = NOSTR; 272 mail1(&head, 0); 273 return(0); 274 } 275 276 277 /* 278 * Send mail to a bunch of user names. The interface is through 279 * the mail routine below. 280 */ 281 int 282 sendmail(str) 283 char *str; 284 { 285 struct header head; 286 287 head.h_to = extract(str, GTO); 288 head.h_subject = NOSTR; 289 head.h_cc = NIL; 290 head.h_bcc = NIL; 291 head.h_smopts = NIL; 292 if ((head.h_replyto = getenv("REPLYTO")) == NULL) 293 head.h_replyto = NOSTR; 294 head.h_inreplyto = NOSTR; 295 mail1(&head, 0); 296 return(0); 297 } 298 299 /* 300 * Mail a message on standard input to the people indicated 301 * in the passed header. (Internal interface). 302 */ 303 void 304 mail1(hp, printheaders) 305 struct header *hp; 306 int printheaders; 307 { 308 char *cp; 309 int pid; 310 char **namelist; 311 struct name *to; 312 FILE *mtf; 313 314 /* 315 * Collect user's mail from standard input. 316 * Get the result as mtf. 317 */ 318 if ((mtf = collect(hp, printheaders)) == NULL) 319 return; 320 if (value("interactive") != NOSTR) { 321 if (value("askcc") != NOSTR) 322 grabh(hp, GCC); 323 else { 324 printf("EOT\n"); 325 (void) fflush(stdout); 326 } 327 } 328 if (fsize(mtf) == 0) { 329 if (hp->h_subject == NOSTR) 330 printf("No message, no subject; hope that's ok\n"); 331 else 332 printf("Null message body; hope that's ok\n"); 333 } 334 /* 335 * Now, take the user names from the combined 336 * to and cc lists and do all the alias 337 * processing. 338 */ 339 senderr = 0; 340 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 341 if (to == NIL) { 342 printf("No recipients specified\n"); 343 senderr++; 344 } 345 /* 346 * Look through the recipient list for names with /'s 347 * in them which we write to as files directly. 348 */ 349 to = outof(to, mtf, hp); 350 if (senderr) 351 savedeadletter(mtf); 352 to = elide(to); 353 if (count(to) == 0) 354 goto out; 355 fixhead(hp, to); 356 if ((mtf = infix(hp, mtf)) == NULL) { 357 fprintf(stderr, ". . . message lost, sorry.\n"); 358 return; 359 } 360 namelist = unpack(cat(hp->h_smopts, to)); 361 if (debug) { 362 char **t; 363 364 printf("Sendmail arguments:"); 365 for (t = namelist; *t != NOSTR; t++) 366 printf(" \"%s\"", *t); 367 printf("\n"); 368 goto out; 369 } 370 if ((cp = value("record")) != NOSTR) 371 (void) savemail(expand(cp), mtf); 372 /* 373 * Fork, set up the temporary mail file as standard 374 * input for "mail", and exec with the user list we generated 375 * far above. 376 */ 377 pid = fork(); 378 if (pid == -1) { 379 warn("fork"); 380 savedeadletter(mtf); 381 goto out; 382 } 383 if (pid == 0) { 384 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)| 385 sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU), 386 fileno(mtf), -1); 387 if ((cp = value("sendmail")) != NOSTR) 388 cp = expand(cp); 389 else 390 cp = _PATH_SENDMAIL; 391 execv(cp, namelist); 392 warn("%s", cp); 393 _exit(1); 394 } 395 if (value("verbose") != NOSTR) 396 (void) wait_child(pid); 397 else 398 free_child(pid); 399 out: 400 (void) Fclose(mtf); 401 } 402 403 /* 404 * Fix the header by glopping all of the expanded names from 405 * the distribution list into the appropriate fields. 406 */ 407 void 408 fixhead(hp, tolist) 409 struct header *hp; 410 struct name *tolist; 411 { 412 register struct name *np; 413 414 hp->h_to = NIL; 415 hp->h_cc = NIL; 416 hp->h_bcc = NIL; 417 for (np = tolist; np != NIL; np = np->n_flink) 418 if ((np->n_type & GMASK) == GTO) 419 hp->h_to = 420 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 421 else if ((np->n_type & GMASK) == GCC) 422 hp->h_cc = 423 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 424 else if ((np->n_type & GMASK) == GBCC) 425 hp->h_bcc = 426 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 427 } 428 429 /* 430 * Prepend a header in front of the collected stuff 431 * and return the new file. 432 */ 433 FILE * 434 infix(hp, fi) 435 struct header *hp; 436 FILE *fi; 437 { 438 register FILE *nfo, *nfi; 439 register int c; 440 int fd; 441 char tempname[PATHSIZE]; 442 443 snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir); 444 if ((fd = mkstemp(tempname)) == -1 || 445 (nfo = Fdopen(fd, "w")) == NULL) { 446 warn("%s", tempname); 447 return(fi); 448 } 449 if ((nfi = Fopen(tempname, "r")) == NULL) { 450 warn("%s", tempname); 451 (void) Fclose(nfo); 452 (void) rm(tempname); 453 return(fi); 454 } 455 (void) rm(tempname); 456 (void) puthead(hp, nfo, 457 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA); 458 c = getc(fi); 459 while (c != EOF) { 460 (void) putc(c, nfo); 461 c = getc(fi); 462 } 463 if (ferror(fi)) { 464 warnx("read"); 465 rewind(fi); 466 return(fi); 467 } 468 (void) fflush(nfo); 469 if (ferror(nfo)) { 470 warn("%s", tempname); 471 (void) Fclose(nfo); 472 (void) Fclose(nfi); 473 rewind(fi); 474 return(fi); 475 } 476 (void) Fclose(nfo); 477 (void) Fclose(fi); 478 rewind(nfi); 479 return(nfi); 480 } 481 482 /* 483 * Dump the to, subject, cc header on the 484 * passed file buffer. 485 */ 486 int 487 puthead(hp, fo, w) 488 struct header *hp; 489 FILE *fo; 490 int w; 491 { 492 register int gotcha; 493 494 gotcha = 0; 495 if (hp->h_to != NIL && w & GTO) 496 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 497 if (hp->h_subject != NOSTR && w & GSUBJECT) 498 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 499 if (hp->h_cc != NIL && w & GCC) 500 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 501 if (hp->h_bcc != NIL && w & GBCC) 502 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 503 if (hp->h_replyto != NOSTR && w & GREPLYTO) 504 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; 505 if (hp->h_inreplyto != NOSTR && w & GINREPLYTO) 506 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; 507 if (gotcha && w & GNL) 508 (void) putc('\n', fo); 509 return(0); 510 } 511 512 /* 513 * Format the given header line to not exceed 72 characters. 514 */ 515 void 516 fmt(str, np, fo, comma) 517 char *str; 518 register struct name *np; 519 FILE *fo; 520 int comma; 521 { 522 register col, len; 523 524 comma = comma ? 1 : 0; 525 col = strlen(str); 526 if (col) 527 fputs(str, fo); 528 for (; np != NIL; np = np->n_flink) { 529 if (np->n_flink == NIL) 530 comma = 0; 531 len = strlen(np->n_name); 532 col++; /* for the space */ 533 if (col + len + comma > 72 && col > 4) { 534 fputs("\n ", fo); 535 col = 4; 536 } else 537 putc(' ', fo); 538 fputs(np->n_name, fo); 539 if (comma) 540 putc(',', fo); 541 col += len + comma; 542 } 543 putc('\n', fo); 544 } 545 546 /* 547 * Save the outgoing mail on the passed file. 548 */ 549 550 /*ARGSUSED*/ 551 int 552 savemail(name, fi) 553 char name[]; 554 register FILE *fi; 555 { 556 register FILE *fo; 557 char buf[BUFSIZ]; 558 register i; 559 time_t now, time(); 560 char *ctime(); 561 562 if ((fo = Fopen(name, "a")) == NULL) { 563 warn("%s", name); 564 return (-1); 565 } 566 (void) time(&now); 567 fprintf(fo, "From %s %s", myname, ctime(&now)); 568 while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 569 (void) fwrite(buf, 1, i, fo); 570 (void) putc('\n', fo); 571 (void) fflush(fo); 572 if (ferror(fo)) 573 warn("%s", name); 574 (void) Fclose(fo); 575 rewind(fi); 576 return (0); 577 } 578