1 /* $OpenBSD: send.c,v 1.23 2014/01/17 18:42:30 okan 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; 134 (c = (unsigned char)*cp++) && c != ':' && !isspace(c); ) 135 ; 136 cp2 = --cp; 137 while (isspace((unsigned char)*cp++)) 138 ; 139 if (cp[-1] != ':') { 140 /* 141 * Not a header line, force out status: 142 * This happens in uucp style mail where 143 * there are no headers at all. 144 */ 145 if (dostat) { 146 if (statusput(mp, obuf, prefix) == -1) 147 goto out; 148 dostat = 0; 149 } 150 if (doign != ignoreall) 151 /* add blank line */ 152 (void)putc('\n', obuf); 153 ishead = 0; 154 ignoring = 0; 155 } else { 156 /* 157 * If it is an ignored field and 158 * we care about such things, skip it. 159 */ 160 *cp2 = 0; /* temporarily null terminate */ 161 if (doign && isign(line, doign)) 162 ignoring = 1; 163 else if (strcasecmp(line, "status") == 0) { 164 /* 165 * If the field is "status," go compute 166 * and print the real Status: field 167 */ 168 if (dostat) { 169 if (statusput(mp, obuf, prefix) == -1) 170 goto out; 171 dostat = 0; 172 } 173 ignoring = 1; 174 } else { 175 ignoring = 0; 176 *cp2 = c; /* restore */ 177 } 178 infld = 1; 179 } 180 } 181 if (!ignoring) { 182 /* 183 * Strip trailing whitespace from prefix 184 * if line is blank. 185 */ 186 if (prefix != NULL) { 187 if (length > 1) 188 fputs(prefix, obuf); 189 else 190 (void)fwrite(prefix, sizeof(*prefix), 191 prefixlen, obuf); 192 } 193 if (dovis) { 194 length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH); 195 (void)fwrite(visline, sizeof(*visline), length, obuf); 196 } else 197 (void)fwrite(line, sizeof(*line), length, obuf); 198 if (ferror(obuf)) 199 goto out; 200 } 201 if (sendsignal == SIGINT) 202 goto out; 203 } 204 /* 205 * Copy out message body 206 */ 207 if (doign == ignoreall) 208 count--; /* skip final blank line */ 209 while (count > 0) { 210 if (fgets(line, sizeof(line), ibuf) == NULL) { 211 c = 0; 212 break; 213 } 214 count -= c = strlen(line); 215 if (prefix != NULL) { 216 /* 217 * Strip trailing whitespace from prefix 218 * if line is blank. 219 */ 220 if (c > 1) 221 fputs(prefix, obuf); 222 else 223 (void)fwrite(prefix, sizeof(*prefix), 224 prefixlen, obuf); 225 } 226 /* 227 * We can't read the record file (or inbox for recipient) 228 * properly with 'From ' lines in the message body (from 229 * forwarded messages or sentences starting with "From "), 230 * so we will prepend those lines with a '>'. 231 */ 232 if (strncmp(line, "From ", 5) == 0) 233 (void)fwrite(">", 1, 1, obuf); /* '>' before 'From ' */ 234 if (dovis) { 235 length = strvis(visline, line, VIS_SAFE|VIS_NOSLASH); 236 (void)fwrite(visline, sizeof(*visline), length, obuf); 237 } else 238 (void)fwrite(line, sizeof(*line), c, obuf); 239 if (ferror(obuf) || sendsignal == SIGINT) 240 goto out; 241 } 242 if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 243 /* no final blank line */ 244 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 245 goto out; 246 rval = 0; 247 out: 248 sendsignal = 0; 249 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 250 (void)sigaction(SIGINT, &saveint, NULL); 251 return(rval); 252 } 253 254 /* 255 * Output a reasonable looking status field. 256 */ 257 int 258 statusput(struct message *mp, FILE *obuf, char *prefix) 259 { 260 char statout[3]; 261 char *cp = statout; 262 263 if (mp->m_flag & MREAD) 264 *cp++ = 'R'; 265 if ((mp->m_flag & MNEW) == 0) 266 *cp++ = 'O'; 267 *cp = 0; 268 if (statout[0]) { 269 fprintf(obuf, "%sStatus: %s\n", 270 prefix == NULL ? "" : prefix, statout); 271 return(ferror(obuf) ? -1 : 0); 272 } 273 return(0); 274 } 275 276 /* 277 * Interface between the argument list and the mail1 routine 278 * which does all the dirty work. 279 */ 280 int 281 mail(struct name *to, struct name *cc, struct name *bcc, struct name *smopts, 282 char *subject) 283 { 284 struct header head; 285 286 head.h_to = to; 287 head.h_subject = subject; 288 head.h_cc = cc; 289 head.h_bcc = bcc; 290 head.h_smopts = smopts; 291 mail1(&head, 0); 292 return(0); 293 } 294 295 296 /* 297 * Send mail to a bunch of user names. The interface is through 298 * the mail routine below. 299 */ 300 int 301 sendmail(void *v) 302 { 303 char *str = v; 304 struct header head; 305 306 head.h_to = extract(str, GTO); 307 head.h_subject = NULL; 308 head.h_cc = NULL; 309 head.h_bcc = NULL; 310 head.h_smopts = NULL; 311 mail1(&head, 0); 312 return(0); 313 } 314 315 /* 316 * Mail a message on standard input to the people indicated 317 * in the passed header. (Internal interface). 318 */ 319 void 320 mail1(struct header *hp, int printheaders) 321 { 322 char *cp; 323 pid_t pid; 324 char **namelist; 325 struct name *to; 326 FILE *mtf; 327 328 /* 329 * Collect user's mail from standard input. 330 * Get the result as mtf. 331 */ 332 if ((mtf = collect(hp, printheaders)) == NULL) 333 return; 334 if (fsize(mtf) == 0) { 335 if (value("skipempty") != NULL) 336 goto out; 337 if (hp->h_subject == NULL || *hp->h_subject == '\0') 338 puts("No message, no subject; hope that's ok"); 339 else 340 puts("Null message body; hope that's ok"); 341 } 342 /* 343 * Now, take the user names from the combined 344 * to and cc lists and do all the alias 345 * processing. 346 */ 347 senderr = 0; 348 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 349 if (to == NULL) { 350 puts("No recipients specified"); 351 senderr++; 352 } 353 /* 354 * Look through the recipient list for names with /'s 355 * in them which we write to as files directly. 356 */ 357 to = outof(to, mtf, hp); 358 if (senderr) 359 savedeadletter(mtf); 360 to = elide(to); 361 if (count(to) == 0) 362 goto out; 363 fixhead(hp, to); 364 if ((mtf = infix(hp, mtf)) == NULL) { 365 fputs(". . . message lost, sorry.\n", stderr); 366 return; 367 } 368 namelist = unpack(hp->h_smopts, to); 369 if (debug) { 370 char **t; 371 372 fputs("Sendmail arguments:", stdout); 373 for (t = namelist; *t != NULL; t++) 374 printf(" \"%s\"", *t); 375 putchar('\n'); 376 goto out; 377 } 378 if ((cp = value("record")) != NULL) 379 (void)savemail(expand(cp), mtf); 380 /* 381 * Fork, set up the temporary mail file as standard 382 * input for "mail", and exec with the user list we generated 383 * far above. 384 */ 385 pid = fork(); 386 if (pid == -1) { 387 warn("fork"); 388 savedeadletter(mtf); 389 goto out; 390 } 391 if (pid == 0) { 392 sigset_t nset; 393 394 sigemptyset(&nset); 395 sigaddset(&nset, SIGHUP); 396 sigaddset(&nset, SIGINT); 397 sigaddset(&nset, SIGQUIT); 398 sigaddset(&nset, SIGTSTP); 399 sigaddset(&nset, SIGTTIN); 400 sigaddset(&nset, SIGTTOU); 401 prepare_child(&nset, fileno(mtf), -1); 402 if ((cp = value("sendmail")) != NULL) 403 cp = expand(cp); 404 else 405 cp = _PATH_SENDMAIL; 406 execv(cp, namelist); 407 warn("%s", cp); 408 _exit(1); 409 } 410 if (value("verbose") != NULL) 411 (void)wait_child(pid); 412 else 413 free_child(pid); 414 out: 415 (void)Fclose(mtf); 416 } 417 418 /* 419 * Fix the header by glopping all of the expanded names from 420 * the distribution list into the appropriate fields. 421 */ 422 void 423 fixhead(struct header *hp, struct name *tolist) 424 { 425 struct name *np; 426 427 hp->h_to = NULL; 428 hp->h_cc = NULL; 429 hp->h_bcc = NULL; 430 for (np = tolist; np != NULL; np = np->n_flink) 431 if ((np->n_type & GMASK) == GTO) 432 hp->h_to = 433 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 434 else if ((np->n_type & GMASK) == GCC) 435 hp->h_cc = 436 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 437 else if ((np->n_type & GMASK) == GBCC) 438 hp->h_bcc = 439 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 440 } 441 442 /* 443 * Prepend a header in front of the collected stuff 444 * and return the new file. 445 */ 446 FILE * 447 infix(struct header *hp, FILE *fi) 448 { 449 FILE *nfo, *nfi; 450 int c, fd; 451 char tempname[PATHSIZE]; 452 453 (void)snprintf(tempname, sizeof(tempname), 454 "%s/mail.RsXXXXXXXXXX", tmpdir); 455 if ((fd = mkstemp(tempname)) == -1 || 456 (nfo = Fdopen(fd, "w")) == NULL) { 457 warn("%s", tempname); 458 return(fi); 459 } 460 if ((nfi = Fopen(tempname, "r")) == NULL) { 461 warn("%s", tempname); 462 (void)Fclose(nfo); 463 (void)rm(tempname); 464 return(fi); 465 } 466 (void)rm(tempname); 467 (void)puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); 468 c = getc(fi); 469 while (c != EOF) { 470 (void)putc(c, nfo); 471 c = getc(fi); 472 } 473 if (ferror(fi)) { 474 warn("read"); 475 rewind(fi); 476 return(fi); 477 } 478 (void)fflush(nfo); 479 if (ferror(nfo)) { 480 warn("%s", tempname); 481 (void)Fclose(nfo); 482 (void)Fclose(nfi); 483 rewind(fi); 484 return(fi); 485 } 486 (void)Fclose(nfo); 487 (void)Fclose(fi); 488 rewind(nfi); 489 return(nfi); 490 } 491 492 /* 493 * Dump the to, subject, cc header on the 494 * passed file buffer. 495 */ 496 int 497 puthead(struct header *hp, FILE *fo, int w) 498 { 499 int gotcha; 500 501 gotcha = 0; 502 if (hp->h_to != NULL && w & GTO) 503 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 504 if (hp->h_subject != NULL && w & GSUBJECT) 505 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 506 if (hp->h_cc != NULL && w & GCC) 507 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 508 if (hp->h_bcc != NULL && w & GBCC) 509 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 510 if (gotcha && w & GNL) 511 (void)putc('\n', fo); 512 return(0); 513 } 514 515 /* 516 * Format the given header line to not exceed 72 characters. 517 */ 518 void 519 fmt(char *str, struct name *np, FILE *fo, int comma) 520 { 521 int col, len; 522 523 comma = comma ? 1 : 0; 524 col = strlen(str); 525 if (col) 526 fputs(str, fo); 527 for (; np != NULL; np = np->n_flink) { 528 if (np->n_flink == NULL) 529 comma = 0; 530 len = strlen(np->n_name); 531 col++; /* for the space */ 532 if (col + len + comma > 72 && col > 4) { 533 fputs("\n ", fo); 534 col = 4; 535 } else 536 putc(' ', fo); 537 fputs(np->n_name, fo); 538 if (comma) 539 putc(',', fo); 540 col += len + comma; 541 } 542 putc('\n', fo); 543 } 544 545 /* 546 * Save the outgoing mail on the passed file. 547 */ 548 /*ARGSUSED*/ 549 int 550 savemail(char *name, FILE *fi) 551 { 552 FILE *fo; 553 char buf[BUFSIZ]; 554 time_t now; 555 mode_t m; 556 557 m = umask(077); 558 fo = Fopen(name, "a"); 559 (void)umask(m); 560 if (fo == NULL) { 561 warn("%s", name); 562 return(-1); 563 } 564 (void)time(&now); 565 fprintf(fo, "From %s %s", myname, ctime(&now)); 566 while (fgets(buf, sizeof(buf), fi) == buf) { 567 /* 568 * We can't read the record file (or inbox for recipient) 569 * in the message body (from forwarded messages or sentences 570 * starting with "From "), so we will prepend those lines with 571 * a '>'. 572 */ 573 if (strncmp(buf, "From ", 5) == 0) 574 (void)fwrite(">", 1, 1, fo); /* '>' before 'From ' */ 575 (void)fwrite(buf, 1, strlen(buf), fo); 576 } 577 (void)putc('\n', fo); 578 (void)fflush(fo); 579 if (ferror(fo)) 580 warn("%s", name); 581 (void)Fclose(fo); 582 rewind(fi); 583 return(0); 584 } 585 586 /*ARGSUSED*/ 587 void 588 sendint(int s) 589 { 590 591 sendsignal = s; 592 } 593