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