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