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