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