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