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