1 /* $NetBSD: send.c,v 1.13 2001/02/05 02:07:53 christos 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.13 2001/02/05 02:07:53 christos 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 *tempMail; 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(mp, obuf, doign, prefix) 65 struct message *mp; 66 FILE *obuf; 67 struct ignoretab *doign; 68 char *prefix; 69 { 70 long count; 71 FILE *ibuf; 72 char line[LINESIZE]; 73 int ishead, infld, ignoring = 0, dostat, firstline; 74 char *cp, *cp2; 75 int c = 0; 76 int length; 77 int prefixlen = 0; 78 79 /* 80 * Compute the prefix string, without trailing whitespace 81 */ 82 if (prefix != NOSTR) { 83 cp2 = 0; 84 for (cp = prefix; *cp; cp++) 85 if (*cp != ' ' && *cp != '\t') 86 cp2 = cp; 87 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; 88 } 89 ibuf = setinput(mp); 90 count = mp->m_size; 91 ishead = 1; 92 dostat = doign == 0 || !isign("status", doign); 93 infld = 0; 94 firstline = 1; 95 /* 96 * Process headers first 97 */ 98 while (count > 0 && ishead) { 99 if (fgets(line, LINESIZE, ibuf) == NULL) 100 break; 101 count -= length = strlen(line); 102 if (firstline) { 103 /* 104 * First line is the From line, so no headers 105 * there to worry about 106 */ 107 firstline = 0; 108 ignoring = doign == ignoreall; 109 } else if (line[0] == '\n') { 110 /* 111 * If line is blank, we've reached end of 112 * headers, so force out status: field 113 * and note that we are no longer in header 114 * fields 115 */ 116 if (dostat) { 117 statusput(mp, obuf, prefix); 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((unsigned char)*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 statusput(mp, obuf, prefix); 146 dostat = 0; 147 } 148 if (doign != ignoreall) 149 /* add blank line */ 150 (void) putc('\n', obuf); 151 ishead = 0; 152 ignoring = 0; 153 } else { 154 /* 155 * If it is an ignored field and 156 * we care about such things, skip it. 157 */ 158 *cp2 = 0; /* temporarily null terminate */ 159 if (doign && isign(line, doign)) 160 ignoring = 1; 161 else if ((line[0] == 's' || line[0] == 'S') && 162 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 statusput(mp, obuf, prefix); 169 dostat = 0; 170 } 171 ignoring = 1; 172 } else { 173 ignoring = 0; 174 *cp2 = c; /* restore */ 175 } 176 infld = 1; 177 } 178 } 179 if (!ignoring) { 180 /* 181 * Strip trailing whitespace from prefix 182 * if line is blank. 183 */ 184 if (prefix != NOSTR) { 185 if (length > 1) 186 fputs(prefix, obuf); 187 else 188 (void) fwrite(prefix, sizeof *prefix, 189 prefixlen, obuf); 190 } 191 (void) fwrite(line, sizeof *line, length, obuf); 192 if (ferror(obuf)) 193 return -1; 194 } 195 } 196 /* 197 * Copy out message body 198 */ 199 if (doign == ignoreall) 200 count--; /* skip final blank line */ 201 if (prefix != NOSTR) 202 while (count > 0) { 203 if (fgets(line, LINESIZE, ibuf) == NULL) { 204 c = 0; 205 break; 206 } 207 count -= c = strlen(line); 208 /* 209 * Strip trailing whitespace from prefix 210 * if line is blank. 211 */ 212 if (c > 1) 213 fputs(prefix, obuf); 214 else 215 (void) fwrite(prefix, sizeof *prefix, 216 prefixlen, obuf); 217 (void) fwrite(line, sizeof *line, c, obuf); 218 if (ferror(obuf)) 219 return -1; 220 } 221 else 222 while (count > 0) { 223 c = count < LINESIZE ? count : LINESIZE; 224 if ((c = fread(line, sizeof *line, c, ibuf)) <= 0) 225 break; 226 count -= c; 227 if (fwrite(line, sizeof *line, c, obuf) != c) 228 return -1; 229 } 230 if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 231 /* no final blank line */ 232 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 233 return -1; 234 return 0; 235 } 236 237 /* 238 * Output a reasonable looking status field. 239 */ 240 void 241 statusput(mp, obuf, prefix) 242 struct message *mp; 243 FILE *obuf; 244 char *prefix; 245 { 246 char statout[3]; 247 char *cp = statout; 248 249 if (mp->m_flag & MREAD) 250 *cp++ = 'R'; 251 if ((mp->m_flag & MNEW) == 0) 252 *cp++ = 'O'; 253 *cp = 0; 254 if (statout[0]) 255 fprintf(obuf, "%sStatus: %s\n", 256 prefix == NOSTR ? "" : prefix, statout); 257 } 258 259 /* 260 * Interface between the argument list and the mail1 routine 261 * which does all the dirty work. 262 */ 263 int 264 mail(to, cc, bcc, smopts, subject) 265 struct name *to, *cc, *bcc, *smopts; 266 char *subject; 267 { 268 struct header head; 269 270 head.h_to = to; 271 head.h_subject = subject; 272 head.h_cc = cc; 273 head.h_bcc = bcc; 274 head.h_smopts = smopts; 275 mail1(&head, 0); 276 return(0); 277 } 278 279 280 /* 281 * Send mail to a bunch of user names. The interface is through 282 * the mail routine below. 283 */ 284 int 285 sendmail(v) 286 void *v; 287 { 288 char *str = v; 289 struct header head; 290 291 head.h_to = extract(str, GTO); 292 head.h_subject = NOSTR; 293 head.h_cc = NIL; 294 head.h_bcc = NIL; 295 head.h_smopts = NIL; 296 mail1(&head, 0); 297 return(0); 298 } 299 300 /* 301 * Mail a message on standard input to the people indicated 302 * in the passed header. (Internal interface). 303 */ 304 void 305 mail1(hp, printheaders) 306 struct header *hp; 307 int printheaders; 308 { 309 char *cp; 310 int pid; 311 char **namelist; 312 struct name *to; 313 FILE *mtf; 314 315 /* 316 * Collect user's mail from standard input. 317 * Get the result as mtf. 318 */ 319 if ((mtf = collect(hp, printheaders)) == NULL) 320 return; 321 if (value("interactive") != NOSTR) { 322 if (value("askcc") != NOSTR || value("askbcc") != NOSTR) { 323 if (value("askcc") != NOSTR) 324 grabh(hp, GCC); 325 if (value("askbcc") != NOSTR) 326 grabh(hp, GBCC); 327 } else { 328 printf("EOT\n"); 329 (void) fflush(stdout); 330 } 331 } 332 if (fsize(mtf) == 0) { 333 if (value("dontsendempty") != NOSTR) 334 goto out; 335 if (hp->h_subject == NOSTR) 336 printf("No message, no subject; hope that's ok\n"); 337 else 338 printf("Null message body; hope that's ok\n"); 339 } 340 /* 341 * Now, take the user names from the combined 342 * to and cc lists and do all the alias 343 * processing. 344 */ 345 senderr = 0; 346 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 347 if (to == NIL) { 348 printf("No recipients specified\n"); 349 senderr++; 350 } 351 /* 352 * Look through the recipient list for names with /'s 353 * in them which we write to as files directly. 354 */ 355 to = outof(to, mtf, hp); 356 if (senderr) 357 savedeadletter(mtf); 358 to = elide(to); 359 if (count(to) == 0) 360 goto out; 361 fixhead(hp, to); 362 if ((mtf = infix(hp, mtf)) == NULL) { 363 fprintf(stderr, ". . . message lost, sorry.\n"); 364 return; 365 } 366 namelist = unpack(cat(hp->h_smopts, to)); 367 if (debug) { 368 char **t; 369 370 printf("Sendmail arguments:"); 371 for (t = namelist; *t != NOSTR; t++) 372 printf(" \"%s\"", *t); 373 printf("\n"); 374 goto out; 375 } 376 if ((cp = value("record")) != NOSTR) 377 (void) savemail(expand(cp), mtf); 378 /* 379 * Fork, set up the temporary mail file as standard 380 * input for "mail", and exec with the user list we generated 381 * far above. 382 */ 383 pid = fork(); 384 if (pid == -1) { 385 perror("fork"); 386 savedeadletter(mtf); 387 goto out; 388 } 389 if (pid == 0) { 390 sigset_t nset; 391 sigemptyset(&nset); 392 sigaddset(&nset, SIGHUP); 393 sigaddset(&nset, SIGINT); 394 sigaddset(&nset, SIGQUIT); 395 sigaddset(&nset, SIGTSTP); 396 sigaddset(&nset, SIGTTIN); 397 sigaddset(&nset, SIGTTOU); 398 prepare_child(&nset, fileno(mtf), -1); 399 if ((cp = value("sendmail")) != NOSTR) 400 cp = expand(cp); 401 else 402 cp = _PATH_SENDMAIL; 403 execv(cp, namelist); 404 perror(cp); 405 _exit(1); 406 } 407 if (value("verbose") != NOSTR) 408 (void) wait_child(pid); 409 else 410 free_child(pid); 411 out: 412 (void) Fclose(mtf); 413 } 414 415 /* 416 * Fix the header by glopping all of the expanded names from 417 * the distribution list into the appropriate fields. 418 */ 419 void 420 fixhead(hp, tolist) 421 struct header *hp; 422 struct name *tolist; 423 { 424 struct name *np; 425 426 hp->h_to = NIL; 427 hp->h_cc = NIL; 428 hp->h_bcc = NIL; 429 for (np = tolist; np != NIL; np = np->n_flink) { 430 if (np->n_type & GDEL) 431 continue; /* Don't copy deleted addresses to the header */ 432 if ((np->n_type & GMASK) == GTO) 433 hp->h_to = 434 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 435 else if ((np->n_type & GMASK) == GCC) 436 hp->h_cc = 437 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 438 else if ((np->n_type & GMASK) == GBCC) 439 hp->h_bcc = 440 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 441 } 442 } 443 444 /* 445 * Prepend a header in front of the collected stuff 446 * and return the new file. 447 */ 448 FILE * 449 infix(hp, fi) 450 struct header *hp; 451 FILE *fi; 452 { 453 FILE *nfo, *nfi; 454 int c; 455 456 if ((nfo = Fopen(tempMail, "w")) == NULL) { 457 perror(tempMail); 458 return(fi); 459 } 460 if ((nfi = Fopen(tempMail, "r")) == NULL) { 461 perror(tempMail); 462 (void) Fclose(nfo); 463 return(fi); 464 } 465 (void) rm(tempMail); 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 perror("read"); 474 rewind(fi); 475 return(fi); 476 } 477 (void) fflush(nfo); 478 if (ferror(nfo)) { 479 perror(tempMail); 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(hp, fo, w) 497 struct header *hp; 498 FILE *fo; 499 int w; 500 { 501 int gotcha; 502 503 gotcha = 0; 504 if (hp->h_to != NIL && w & GTO) 505 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 506 if (hp->h_subject != NOSTR && w & GSUBJECT) 507 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 508 if (hp->h_cc != NIL && w & GCC) 509 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 510 if (hp->h_bcc != NIL && w & GBCC) 511 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 512 if (gotcha && w & GNL) 513 (void) putc('\n', fo); 514 return(0); 515 } 516 517 /* 518 * Format the given header line to not exceed 72 characters. 519 */ 520 void 521 fmt(str, np, fo, comma) 522 char *str; 523 struct name *np; 524 FILE *fo; 525 int comma; 526 { 527 int col, len; 528 529 comma = comma ? 1 : 0; 530 col = strlen(str); 531 if (col) 532 fputs(str, fo); 533 for (; np != NIL; np = np->n_flink) { 534 if (np->n_flink == NIL) 535 comma = 0; 536 len = strlen(np->n_name); 537 col++; /* for the space */ 538 if (col + len + comma > 72 && col > 4) { 539 fputs("\n ", fo); 540 col = 4; 541 } else 542 putc(' ', fo); 543 fputs(np->n_name, fo); 544 if (comma) 545 putc(',', fo); 546 col += len + comma; 547 } 548 putc('\n', fo); 549 } 550 551 /* 552 * Save the outgoing mail on the passed file. 553 */ 554 555 /*ARGSUSED*/ 556 int 557 savemail(name, fi) 558 char name[]; 559 FILE *fi; 560 { 561 FILE *fo; 562 char buf[BUFSIZ]; 563 int i; 564 time_t now; 565 566 if ((fo = Fopen(name, "a")) == NULL) { 567 perror(name); 568 return (-1); 569 } 570 (void) time(&now); 571 fprintf(fo, "From %s %s", myname, ctime(&now)); 572 while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 573 (void) fwrite(buf, 1, i, fo); 574 (void) putc('\n', fo); 575 (void) fflush(fo); 576 if (ferror(fo)) 577 perror(name); 578 (void) Fclose(fo); 579 rewind(fi); 580 return (0); 581 } 582