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