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 int pid; 307 char **namelist; 308 struct name *to; 309 FILE *mtf; 310 311 /* 312 * Collect user's mail from standard input. 313 * Get the result as mtf. 314 */ 315 if ((mtf = collect(hp, printheaders)) == NULL) 316 return; 317 if (value("interactive") != NULL) { 318 if (value("askcc") != NULL || value("askbcc") != NULL) { 319 if (value("askcc") != NULL) 320 grabh(hp, GCC); 321 if (value("askbcc") != NULL) 322 grabh(hp, GBCC); 323 } else { 324 printf("EOT\n"); 325 (void)fflush(stdout); 326 } 327 } 328 if (fsize(mtf) == 0) { 329 if (value("dontsendempty") != NULL) 330 goto out; 331 if (hp->h_subject == NULL) 332 printf("No message, no subject; hope that's ok\n"); 333 else 334 printf("Null message body; hope that's ok\n"); 335 } 336 /* 337 * Now, take the user names from the combined 338 * to and cc lists and do all the alias 339 * processing. 340 */ 341 senderr = 0; 342 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 343 if (to == NULL) { 344 printf("No recipients specified\n"); 345 senderr++; 346 } 347 /* 348 * Look through the recipient list for names with /'s 349 * in them which we write to as files directly. 350 */ 351 to = outof(to, mtf, hp); 352 if (senderr) 353 savedeadletter(mtf); 354 to = elide(to); 355 if (count(to) == 0) 356 goto out; 357 fixhead(hp, to); 358 if ((mtf = infix(hp, mtf)) == NULL) { 359 fprintf(stderr, ". . . message lost, sorry.\n"); 360 return; 361 } 362 namelist = unpack(cat(hp->h_smopts, to)); 363 if (debug) { 364 char **t; 365 366 printf("Sendmail arguments:"); 367 for (t = namelist; *t != NULL; t++) 368 printf(" \"%s\"", *t); 369 printf("\n"); 370 goto out; 371 } 372 if ((cp = value("record")) != NULL) 373 (void)savemail(expand(cp), mtf); 374 /* 375 * Fork, set up the temporary mail file as standard 376 * input for "mail", and exec with the user list we generated 377 * far above. 378 */ 379 pid = fork(); 380 if (pid == -1) { 381 warn("fork"); 382 savedeadletter(mtf); 383 goto out; 384 } 385 if (pid == 0) { 386 sigset_t nset; 387 (void)sigemptyset(&nset); 388 (void)sigaddset(&nset, SIGHUP); 389 (void)sigaddset(&nset, SIGINT); 390 (void)sigaddset(&nset, SIGQUIT); 391 (void)sigaddset(&nset, SIGTSTP); 392 (void)sigaddset(&nset, SIGTTIN); 393 (void)sigaddset(&nset, SIGTTOU); 394 prepare_child(&nset, fileno(mtf), -1); 395 if ((cp = value("sendmail")) != NULL) 396 cp = expand(cp); 397 else 398 cp = _PATH_SENDMAIL; 399 execv(cp, namelist); 400 warn("%s", cp); 401 _exit(1); 402 } 403 if (value("verbose") != NULL) 404 (void)wait_child(pid); 405 else 406 free_child(pid); 407 out: 408 (void)Fclose(mtf); 409 } 410 411 /* 412 * Fix the header by glopping all of the expanded names from 413 * the distribution list into the appropriate fields. 414 */ 415 void 416 fixhead(hp, tolist) 417 struct header *hp; 418 struct name *tolist; 419 { 420 struct name *np; 421 422 hp->h_to = NULL; 423 hp->h_cc = NULL; 424 hp->h_bcc = NULL; 425 for (np = tolist; np != NULL; np = np->n_flink) { 426 /* Don't copy deleted addresses to the header */ 427 if (np->n_type & GDEL) 428 continue; 429 if ((np->n_type & GMASK) == GTO) 430 hp->h_to = 431 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 432 else if ((np->n_type & GMASK) == GCC) 433 hp->h_cc = 434 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 435 else if ((np->n_type & GMASK) == GBCC) 436 hp->h_bcc = 437 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 438 } 439 } 440 441 /* 442 * Prepend a header in front of the collected stuff 443 * and return the new file. 444 */ 445 FILE * 446 infix(hp, fi) 447 struct header *hp; 448 FILE *fi; 449 { 450 FILE *nfo, *nfi; 451 int c, fd; 452 char tempname[PATHSIZE]; 453 454 (void)snprintf(tempname, sizeof(tempname), 455 "%s/mail.RsXXXXXXXXXX", tmpdir); 456 if ((fd = mkstemp(tempname)) == -1 || 457 (nfo = Fdopen(fd, "w")) == NULL) { 458 warn("%s", tempname); 459 return (fi); 460 } 461 if ((nfi = Fopen(tempname, "r")) == NULL) { 462 warn("%s", tempname); 463 (void)Fclose(nfo); 464 (void)rm(tempname); 465 return (fi); 466 } 467 (void)rm(tempname); 468 (void)puthead(hp, nfo, 469 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA); 470 c = getc(fi); 471 while (c != EOF) { 472 (void)putc(c, nfo); 473 c = getc(fi); 474 } 475 if (ferror(fi)) { 476 warnx("read"); 477 rewind(fi); 478 return (fi); 479 } 480 (void)fflush(nfo); 481 if (ferror(nfo)) { 482 warn("%s", tempname); 483 (void)Fclose(nfo); 484 (void)Fclose(nfi); 485 rewind(fi); 486 return (fi); 487 } 488 (void)Fclose(nfo); 489 (void)Fclose(fi); 490 rewind(nfi); 491 return (nfi); 492 } 493 494 /* 495 * Dump the to, subject, cc header on the 496 * passed file buffer. 497 */ 498 int 499 puthead(hp, fo, w) 500 struct header *hp; 501 FILE *fo; 502 int w; 503 { 504 int gotcha; 505 506 gotcha = 0; 507 if (hp->h_to != NULL && w & GTO) 508 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 509 if (hp->h_subject != NULL && w & GSUBJECT) 510 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 511 if (hp->h_cc != NULL && w & GCC) 512 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 513 if (hp->h_bcc != NULL && w & GBCC) 514 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 515 if (hp->h_replyto != NULL && w & GREPLYTO) 516 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++; 517 if (hp->h_inreplyto != NULL && w & GINREPLYTO) 518 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++; 519 if (gotcha && w & GNL) 520 (void)putc('\n', fo); 521 return (0); 522 } 523 524 /* 525 * Format the given header line to not exceed 72 characters. 526 */ 527 void 528 fmt(str, np, fo, comma) 529 const char *str; 530 struct name *np; 531 FILE *fo; 532 int comma; 533 { 534 int col, len; 535 536 comma = comma ? 1 : 0; 537 col = strlen(str); 538 if (col) 539 fputs(str, fo); 540 for (; np != NULL; np = np->n_flink) { 541 if (np->n_flink == NULL) 542 comma = 0; 543 len = strlen(np->n_name); 544 col++; /* for the space */ 545 if (col + len + comma > 72 && col > 4) { 546 fprintf(fo, "\n "); 547 col = 4; 548 } else 549 fprintf(fo, " "); 550 fputs(np->n_name, fo); 551 if (comma) 552 fprintf(fo, ","); 553 col += len + comma; 554 } 555 fprintf(fo, "\n"); 556 } 557 558 /* 559 * Save the outgoing mail on the passed file. 560 */ 561 562 /*ARGSUSED*/ 563 int 564 savemail(name, fi) 565 char name[]; 566 FILE *fi; 567 { 568 FILE *fo; 569 char buf[BUFSIZ]; 570 int i; 571 time_t now; 572 573 if ((fo = Fopen(name, "a")) == NULL) { 574 warn("%s", name); 575 return (-1); 576 } 577 (void)time(&now); 578 fprintf(fo, "From %s %s", myname, ctime(&now)); 579 while ((i = fread(buf, 1, sizeof(buf), fi)) > 0) 580 (void)fwrite(buf, 1, i, fo); 581 fprintf(fo, "\n"); 582 (void)fflush(fo); 583 if (ferror(fo)) 584 warn("%s", name); 585 (void)Fclose(fo); 586 rewind(fi); 587 return (0); 588 } 589